Scratch pad

An overview of what I want to do with this game

I think the combination of hack/slash (a la Diablo) and a mix of conversational adventure game is something that I’m trying to hit. I’d like to see the player interact with the world through NPCs and manipulating the environment to a certain extent. I’d like the player to have a variety of missions to complete that makes up the overall narrative. I’d like there to be some skill-based real-time combat like Diablo. Therefore, there should be a skill system as well.

These are some aspects of the game I’d like to see.

Talk

I’d like to do some conversational adventure, which means some sort of dialogue between player and NPC. The most accessible format at the moment is multiple choice dialogue.

NPCs will give descriptions about the world, specifically ‘day in the life’.

The player will be presented with pre-determined choices and the NPC will reply. The Talk aspect is more to reveal a deeper storyline than graphics; the Talk system won’t be a adventure puzzle to figure out.

Inventory

Player has an inventory of items to use.

Player can receive items from NPC talks, and get items from places, and robots, and NPCs, too.

Player can use items in several contexts: combat, adventure, talk.

In combat, weapons, protective devices, and combat utility devices are used.

In adventure, adventure items are used, such as keys, and any such tagged.

In talk, adventure items are used, but in the context of a conversation, such as a mission item where in act of dialogue, an item is traded implicitly.

Combat+Skill

Combat is skill-based. Combat is divided into these aspects.

Offense

There are a few weapons classes available for the player: small, medium, large. These only refer to the physical aspect of the weapon (as it may be represented in the player sprite). Large weapons cannot be ‘holstered’ or hidden.

In addition to the size, a weapon can be an energy type or projectile type.

The possible effects of an offensive weapon can be to damage, or stun (incapacitate):

Damaging is easier but the disadvantage is not being able to salvage as much from the robot afterwards.

Stunning is harder to do, but salvage is more.

Drawing a weapon takes time, so there is the aspect of when to draw a weapon. Smaller weapons are quicker to draw.

Weapons have attributes that define their combat rolls.

Defense

This is mostly passive attribute. Defense includes damage resistance, and hit evasion. It can be re-termed as toughness and agility, respectively.

Toughness and agility improved by skill and by items.

Stealth

Stealth is the third aspect of combat and is unique is a sense that it avoids damage by avoiding combat.

Stealth is the ability of the player to move in threat areas and not be attacked by robots.

Stealth can be improved by skill and by items. The ultimate stealth item is an invisibility cloak which allows the player to pass across a robot on an adjacent tile.

Stealth is also dependent upon the weapon drawn, if any.

Missions (accomps)

Missions relate to actions. This may involve combat (eg destroy robot), or something adventure related (eg destroy terminal, talk to NPC, place item on table, etc).

Here is a sample list of possibles:

  • Destroy computer terminal (requires being able to fire at a terminal)
  • Hack computer terminal (hacking action)
  • Receive item from NPC (via Talk)
  • Plug data storage into terminal (use item action)
  • Free prisoner (involves a sequence of accomps such as hacking prisoner doors, talking to prisoner,  concluding talk with prisoner)
  • Navigate to certain position (positional)

So, in a sense, missions are oriented towards objects. Ie:

  • Attackables – those particular entities that need to be destroyed/disabled in order to accomplish the mission
  • Useitemables – entities that need a particular item to be used on them
  • Talkables – entities that need to be talked to
  • Hackables – similar to Useitemables, Hackables are particularly oriented towards whether an entity was successfully hacked.
  • Positional – an exception to above, an accomplishment can be marked down when the player reaches a certain place. In addition, a Positional accomp might be that the player needs to remain in the position to keep the accomp true, for example, in the case of exiting an area to escape.

 

Adventure

Adventure vs combat: the player has the option of drawing his weapon, which makes robots and SCAMs see him as a threat, but not other humans.  Only small and medium weapons can be hidden. Large weapons are always drawn (too big to hide).

Salvaging: this is the act of looting a disabled or destroyed robot.

Hacking: this is robot land, but player is a miner, so hacking is dependent upon the possession of Haxboxes, which aid in the hacking of terminals.


Hacking (Useitemables)

Hacking is like using at item on an object, but requires a Haxbox, so this is the constant thing. The hackable object will then be tagged as hacked if successful.

Larger-than-tile-size images

Placing Tiles which are bigger than the Map’s tile size. A reference in the Tiled forums.

Summary of the issue is that multi-selecting tiles in while in Isometric projection does not yield a ‘flat’ orthographic placement of the tile, obviously. Ideally, a multi-selected Tile should be presented as a flat screen-space graphic, but the proper procedure, as Bjorn pointed out in the link above is to use a Collection of Images, so as to obviate the need to select multiple tiles to get to a bigger image.

This is fine in respect to the OP’s problem of putting in establishments. But my issues are putting walls, and all sorts of tiles that need to be connected precisely unto other tiles. The problem is how tiles are aligned in the first place. Tiles are anchored at the lower-left point. That means the bigger the tile gets in relation to the map tile size, the further up and right the tile’s registration will be.

2017-02-15-18_08_34-_untitled-tmx-tiled

On the outer hand, an Image Object aligns itself with the bottom anchor point making it ideal as a positioning entity.

I went ahead and modified TMX Importer V2 to retrieve the tileset id or image used by the Image Object, but Rex has also modified it after requesting it, so either way this is a doable solution.

 

Example requirements

  1. On LMB on NPC, walk and talk to NPC.
  2. On LMB on InvItem, walk and take InvItem.
  3. On LMB on Door, walk to Door and enter Room.
  4. Series of animation played end-to-end. (Use FSM?) (Determining end of animation)

Rex’s Scenario plugins

The Scenario plugin and its Behaviour counterpart need to be looked at in more detail.


Doors example

In the case of Doors, the PC can move onto the same Tile where Doors lie on, because Doors are rendered at the edges of the Tiles rather than at the centre of the tile (where the PC cannot be feasible rendered on top of it).

Portals exist for Doors; Portals are in the same position as the Door Sprite they belong to. When a Door Sprite is clicked, the PC Pathfinds to the Tile.


InvItem example

An InvItem, like a Door, exists on a Tile that the PC can move onto. Thus, the Tile is associated with InvItem directly.


NPC example

For NPCs, the PC cannot move on to the same Tile. If an NPC is clicked, this doesn’t automatically intend to Talk, but could be a move to the NPC position. However, the PC cannot move onto the same Tile as the NPC. Furthermore, the PC may not be able to reach a Tile that is adjacent to the NPC.

If PC intends to talk, the PC Pathfinds to the NPC.

Creating NPCs and NPC TalkPoints

IN TILED
NPCs

Create a Tiled Object type ‘npc’. I’m choosing to use Objects instead of a Tile in Tiled is because using a Tile will require a separate Tile Layer. In the processing of the TMX, it seems more clean/straightforward to process the Objects separately, so that the placement of the NPCs can occur on the Main layer (where Z-Sorting occurs).

The NPC will have the property called autoTP.

NPC.autoTP=-1 # -1 means NPC cannot be talked to
NPC.autoTP=0 # 0 means manually placed TalkPoint is expected by system
NPC.autoTP=1 # 1 means system will automatically generate TalkPoints based on accessibility of neighbouring Tiles

It will also have a ‘name’ property which denotes a unique string name for the NPC.

NPC.name='Letigus'
TalkPoints

If NPC.autoTP is = 0, then a TalkPoint in Tiled is expected to be manually placed. Create a Tiled Object type ‘talkpoint’. The name can be anything (as the property is read, not the name). Eg ‘tp1’.

A TalkPoint has a property called ‘npc’, which refers to the NPC.

TalkPoint.npc='Letigus' # specified in Tiled as this associated must be made during design
# 'Letigus', obviously, as an NPC Object, must exist in the Tiled level as well
IN C2
Prep NPCs

C2 NPCs are composed of a generic ‘mover’ entity (NPCMover), and a unique ‘graphic’ entity (NPCSprite) that belongs to a Family (NPCSpriteFamily), both of which are Sprite C2 Objects.

/NPCMover
./NPCLegtigusSprite -> NPCSpriteFamily

NPCMover has a ‘name’ variable for the name of the NPC it is associated with.

NPCMover.name='Letigus' # this is populated when TMX is loaded
NPCMover.sprite=-1 # this is NPCSpriteFamily.UID when it is generated

NPCSpriteFamily will have ‘name’ variable denoting the NPC it refers to; a ‘talkpoint’ variable pointing to to the UID of the Tile which is on the TalkPoint when/if it is made.

NPCSpriteFamily.f_name='Letigus' # Family variable init manually based on the Sprite's graphic
NPCSpriteFamily.f_talkpoint=-1 # represents a Tile UID, valid only if .autoTP is 0 (manually placed TalkPoints)
NPCSpriteFamily.f_autoTP=-1 # refer to above for value meanings
NPCSpriteFamily.f_mover=-1 # this is the association to a particular mover, which is the positional marker
Prep TalkPoints

TalkPoints in C2 are expressed within Tiles. Tiles in the 0-layer are tagged with TalkPoint names, but init with blank strings (”)

Tile.talkpoint='Letigus' # when PC touches this Tile and queries it for Talking, it will return 'Letigus'.
Tile.talkpoint=''

So a TalkPoint is not really a separated entity as such in C2, but rather, it is embedded in the Tiles since those are the ones being queried.

TMX Loading

This is the procedure during C2 TMX loading:

-Scan for Tiled Objects with type 'npc'
-Query 'name' of 'npc'
-Query 'autoTP' of 'npc'; for now assume .autoTP=0
-Query logical position of object
-Look for 'name' in NPCSpriteFamily.f_name, assume found
-Instantiate NPCMover on Main layer, and assign NPCMover.name = NPCSpriteFamily.f_name
-Assign NPCMover.sprite = NPCSpriteFamily.UID
-Assign NPCSpriteFamily.f_mover = NPCMover.UID
-Assign NPCSpriteFamily.f_autoTP 'autoTP' value of 'npc'
-Position NPCMover on Tile
-Instantiate that NPCSprite on Main layer (use Nickname to instantiate; see here)
-Pin NPCSprite to NPCMover (no offset)

-Scan for Tiled Objects with type 'talkpoint'
-Query logical position of object
-Query 'name' of 'talkpoint'
-Select the Tile resides on logical position of 'talkpoint' at 0-layer
-Assign Tile.talkpoint the 'name' of the 'talkpoint'

C2 Auto-placement of TalkPoints

Assuming there had been some NPCs with an ‘autoTP’ value of 1, this means that the system will generate automatic TalkPoints for that NPC. This happens after TMX loading.

-Loop through NPCSpriteFamily with .f_autoTP=1
-Select the NPCMover based on NPCSpriteFamily.f_mover
-Determine the Tile ID that the NPCMover is on
-Determine the Tiles that surround the Tile in question
-Cull out the Tiles that are not accessible (impass=true)
-Mark those Tiles with Tiles.talkpoint='Letigus' (NPCSpriteFamily.f_name)
 IN ACTION IN C2
  • When an NPCSpriteFamily is clicked, is intent=’talk='<npc_name>”?
    • Yes: Query current Tile. Is Tile.talkpoint = NPCSpriteFamily.f_name?
      • Yes: Talk.
      • No: Is NPCSpriteFamily.f_autoTP=0? (Manually placed TalkPoints)
        • Yes: Select Tile with Tile.UID = NPCSpriteFamily.f_talkpoint, and Pathfind to Tile
        • No: Pathfind to NPC location
    • No: Pathfind to NPC location
  • On enter Tile
    • Query if Tile.talkpoint != ”?
      • Yes (it is a TalkPoint): Query if intent=’talk’ and if intent=’talk=<Tile.talkpoint>’ (intent is to talk, and to talk to that specific NPC)
        • Yes: Stop and talk.
        • No: do nothing.
      • No (it is not a TalkPoint): do nothing.

OTHER ACTIONABLE OBJECTS

Find other examples that use the same concept as ‘click here, move there’, like in the NPC, where clicking on the NPC will not necessarily Pathfind to the NPC, but to a TalkPoint.


ACTIONS

Actions upon Objects can consist of

  1. Talk
  2. Look (examine)
  3. Use (take/get)

Talk has been previously covered

Look

As with StorageDict, which is first example of the the persistent data implementation, we create a LookDict Object. A LookDict has the following variables

LookDict.alias='l#.r#' # string id and room id to uniquely identify itself; this is generated by self.name+self.room
LookDict.name='l#' # string id for name only
LookDict.room='r#' # string id for room only
LookDict.looktimes=0 # number of times that this item has been looked on
LookDict.textdict=-1 # the UID of the LookTextDict that this LookDict refers to

The first 3 variables are identical to StorageDict.

The .lookTimes variable is new, and this stores the number of times the associated Object has been looked at. This is in consideration that the description of something may change by the number of times the player decides to examine an Object. This is also stored in the LookDict (the one that will be saved with the game) so that the value can be persistent.

The .textDict variable is new, too. Because the nature of reading out a description potentially need long text, the best way is to use a CSV to write the text script out in a spreadsheet, instead of trying to do it in Tiled. So a LookTextDict Object is created to store static text from a CSV string. This LookTextDict is then associated with the LookDict that asked for it to be created. Both the LookDict and LookTextDict are persistent.

The LookTextDict have instance variables:

LookTextDict.alias = LookDict.alias
LookTextDict.lookdict = LookDict.UID

At this point I’m not sure if .alias is needed, but it is important that the LookTextDict is associated with its LookText via .UID.

The LookTextDict (Dictionary) has a certain syntax with its keys.

LookTextDict['n:0'] = 'text to show by default'
LookTextDict['n:3'] = 'text to show if player has looked on this object at least 3 times'
LookTextDict['a:ate cupcake'] = 'text to show if the accomps "ate cupcake" is achieved'

In the case of the above, if the player has looked onto the object 2 times, then the text in [‘n:0’] is shown since it has not reached 3 times.

Accomps (Accomplishments) are tracked differently (in the Player Object itself) so there’s no need to track persistent data in LookDict at all.

Again, the difference between LookDict and LookTextDict:

  • LookDict stores persistence data and is generated by the TMX Object ‘look’
  • LookTextDict is a static Dictionary generated at the beginning of the game (irrespective of TMX loading). It is a Dict that is init based on the contents of a much wider global ‘Look Text description CSV file’. LookDict looks for its LookTextDict partner through alias identification; when LookDict finds its LookTextDict partner, they make a connection through populating their .textdict and .lookdict variables, respectively, with their partner’s UID.

Implementation

In Tiled, create an Object type ‘look’. There are no other properties needed, only that its name is important as a lookup value for the CSV.

Externally, create a CSV/spreadsheet referencing the Tiled Object ‘look’ name.

In C2, LookTextDict Objects are created based on the CSV. So at the beginning of the load, perhaps even before the TMX is loaded, LookTextDict Objects are created ready to be referenced by and ‘look’ Object looking for its partner.

(Consider that small Sprites even within the same Tile (or not necessarily corresponding to a Tile) need a Look description)

 

Use

Using a locked door as an example, the locked Door can be opened by:

  1. Secpass (InvItem)
  2. Accomps

In the case of InvItem, that hasn’t been designed yet.

Accomps are easy, because the lookup is in the global AccompsDict. If an accomps key is encountered, then that locked Door can be opened.

Portals as Doors

Since Portals contain the ‘destination’ property, Portals can be promoted to take more properties. But to keep the data concentrated on one place, let the extra properties be put in the PortalDict.

Portal.name
PortalDict.destination
PortalDict.locked # is the Portal locked?
PortalDict.unlockreq # requirements to unlock this Portal

Note that the ‘destination’ has been put in the PortalDict, instead of the Portal.

Although .destination and .unlockreq may be considered as static data putting every relevant data in one Object is much clearer. So here we have a mix of static and persistent/volatile data in the PortalDict Object.

.locked should be persistent. And using the same principle as the others, PortalDict is created to keep track of the Portal’s data.

Currently, a Portal is read from the TMX, then a Portal Sprite is created in the Portal z-layer.

So now, when a Portal is read, it goes through the same procedure, but a PortalDict is created as well. I don’t think a Dictionary is needed to carry simple information, I’m just trying to be consistent with the C2 Object types. And besides, there may be further use of expanding the data it can hold, and a Dictionary is the best container for that.

PortalDict, thus, needs an .alias/.name/.room instance variable to identify itself uniquely. It also needs a pointer to the UID of the Portal Sprite it’s associated with

PortalDict.alias
PortalDict.name
PortalDict.room
PortalDict.portal

Note that the current Portal Sprite considers ‘name’ to be what we now call ‘alias’ (eg ‘p1.r1’). This must be changed when we start implementing PortalDict.

Conversely, the Portal Sprite must have a PortalDict connection:

Portal.portaldict

So, to recap TMX loading:

  1. On TMX load Portal Objects are read.
  2. Portal Sprites are generated on the ‘Portal’ z-layer of the chess/tile.
  3. PortalDict is generated from Portal.alias. If PortalDict already exists, then make the connection by PortalDict.portal = Portal.UID, and Portal.portaldict = PortalDict.UID.
  4. If PortalDict does not exist, then create a new one; assign PortalDict.alias=Portal.alias, and trade UIDs between them (like in #3 above)

In action in C2:

  1. When the player clicks on a Door, the intent could be to open. But PC will approach first if not on the Tile itself.
  2. Regardless, an ‘enter’ action is needed to test the Portal’s accessibility.
  3. The Portal being queried will be Portal Object residing in the ‘Portal’ z-layer that the PC is currently on.
  4. Is Portal.locked=true?
    1. Yes: what is Portal.unlockreq?
      1. Parsing the unlockreq. Note that at the moment there are two possible ways of unlocking a Portal: by InvItem, and by Accomps. Use ‘i’ as the key for InvItem, and use ‘a’ as the key for Accomps. Eg: Portal.unlockreq=’a:ate cupcake’, or Portal.unlockreq=’i:cupcake’, or an AND combination: Portal.unlockreq=’i:coffee;a:ate cupcake’.
    2. No: enter the Portal destination

 

%d bloggers like this: