Goals
This little article has significantly fewer pictures than the previous one, sorry if that disappoints/bores you. There are almost 500 lines of code in here if that does anything for you.
Today we'll be adding inventory management to the demo, this includes:
1) Adding & removing items from the player's inventory
2) Displaying a give/take dialog where the player can choose items
3) Responding to a selection from the previous step
Alright, let's get going...
Inventory management
First off is defining the maximum number of items the player can carry. This is small for two reasons: laziness. Well, mostly laziness. I was going to add an excuse that it makes the game more realistic if the player can't carry 99 potions but I assume most of you would see through that.
MAX_ITEMS=$04 ; maximum number of items the player can carry
Next is reserving some places in memory to store the item list:
;-----------------
; player inventory
;-----------------
MEM_PLAYER_ITEM_COUNT=$FFFF002C ; how many items the player is holding
MEM_PLAYER_ITEMS=$FFFF002E ; item list
Now we need the basic add & remove subroutines. Add is first for hopefully obvious reasons:
;---------------------------------------
; adds an item to the player's inventory
; d7 = item to add
; d6 is modified by this subroutine
; a1 is modified by this subroutine
;---------------------------------------
AddItem:
move.w (MEM_PLAYER_ITEM_COUNT),d6 ; copy current item count to d6
cmpi.w #MAX_ITEMS,d6 ; check if inventory is full
bge.s ExitAddItem ; branch if already at the limit
addq #$1,(MEM_PLAYER_ITEM_COUNT) ; increment item count
mulu.w #WORD_SIZE,d6 ; find index of location to write the item
lea MEM_PLAYER_ITEMS,a1 ; point to start of item list
move.w d7,(a1,d6) ; copy the item
ExitAddItem:
rts
Before we can remove an item we need a subroutine to determine if the player is carrying it in the first place. "Remove by index" is an easy enough subroutine to add but not as useful. There are a dozen scenarios where removing an item by item ID makes sense. Anyway, this subroutine checks if the player has an item and if so returns the index. -1 (FFFF) is returned if the item is not found.
;---------------------------------------------------
; tests if the player has an item
; d7 = item to look for
; d6 will have index of item if found, otherwise FFFF
; d5 is modified by this subroutine
; a1 is modified by this subroutine
;-----------------------------------------------------
HasItem:
move.w (MEM_PLAYER_ITEM_COUNT),d6 ; copy item count to d6
tst.w d6 ; test if item count is zero
beq.s HasItemNotFound ; item count is zero, return FFFF and exit
subq #$1,d6 ; decrement for zero-based list
move.w d6,d5 ; copy item count to d5
mulu.w #WORD_SIZE,d5 ; multiply to get the index of the last item
lea MEM_PLAYER_ITEMS,a1 ; point to start of item list
adda.l d5,a1 ; move a1 to last item in the list
HasItemLoop:
cmp.w (a1),d7 ; test if this is the item we're looking for
beq.s ExitHasItem ; branch if it is
suba.l #WORD_SIZE,a1 ; move to next item
dbra d6,HasItemLoop ; loop to the end of the list
HasItemNotFound:
move.w #$FFFF,d6 ; set FFFF as the return value
ExitHasItem:
rts
Last is the subroutine to remove an item, which calls the previous subroutine:
;--------------------------------------------
; removes an item from the player's inventory
; d7 = item to remove
; d5 and d6 are modified by this subroutine
; a1 is modified by this subroutine
;--------------------------------------------
RemoveItem:
bsr.w HasItem ; test if the item is in the inventory
cmpi.w #$FFFF,d6 ; test if the item was found
beq.s ExitRemoveItem ; exit if it was not
subq #$1,(MEM_PLAYER_ITEM_COUNT) ; decrement item count
move.w d6,d5 ; copy item index to d5
mulu.w #WORD_SIZE,d5 ; multiply to get the index of the removed item
lea MEM_PLAYER_ITEMS,a1 ; point to start of item list
adda.l d5,a1 ; move a1 to the removed item
move.w #$0000,(a1) ; clear removed item
move.w (MEM_PLAYER_ITEM_COUNT),d5 ; copy item count to d5
sub.w d6,d5 ; subtract item index from count to get loop control
beq.s ExitRemoveItem ; exit if the only item in inventory was removed
RemoveItemLoop:
move.w (WORD_SIZE,a1),(a1) ; copy item +1 to current item
adda.l #WORD_SIZE,a1 ; move to next item
move.w #$0000,(a1) ; clear item slot
dbra d5,RemoveItemLoop ; loop to next item
ExitRemoveItem:
rts
With the basic plumbing out of the way we can move on to the slightly more difficult part...
Item dialog
In previous articles we built dialogs with two and three selections. We now need a dialog that displays four selections. This magical number is based on the number of items in the player's inventory. Although it could page to fit more if needed. It will also be used when the player selects "Take" from the A button menu to present them with a list of items to choose. Also, I think it will be helpful at some point to have a dialog that gives the player four choices. Like in a battle it could show options like: Fight, Item, Defend, or Wet Pants.
Step one is adding some new dialog flags and renaming a couple others:
DIALOG_FLAG_STYLE_ITEM_MENU=$15 ; item menu dialog
DIALOG_FLAG_STYLE_MENU=$16 ; overworld menu style dialog
DIALOG_FLAG_STYLE_TEXT_4CHOICE=$17 ; dialog has text with selection at the end
DIALOG_FLAG_STYLE_TEXT_2CHOICE=$18 ; dialog has text with selection at the end
DIALOG_FLAG_STYLE_TEXT_4CHOICE is used mostly when the selector sprite is moved. DIALOG_FLAG_STYLE_ITEM_MENU tells the ProcessDialog subroutine to draw a list of items. It also changes the values set when the dialog is closed.
For ProcessDialog to handle items we need three new variables. We need a pointer to a list of items, a counter of where we are in drawing that list, and also a place to store the item selected.
MEM_ACTIVE_ITEM=$FFFF001A ; item that is currently being used
[...]
MEM_DIALOG_ITEM_LIST=$FFFF0046 ; pointer to location of item list to display
MEM_DIALOG_ITEM_INDEX=$FFFF004A ; track which item in the list is being drawn
The nice thing about MEM_DIALOG_ITEM_LIST is that it can either point to an address in the ROM or in memory so it can be used to display static items or what the player is currently carrying.
Now we need some items. Since the player starts off close to the magazine rack, and I'm incredibly lazy, the first items will be magazines. This requires creating some item IDs, names for the items, and a lookup table for the names:
;-----------------------------------
; items
;-----------------------------------
OBJ_ITEM_BASE=$3000
OBJ_ITEM_NOTHING=OBJ_ITEM_BASE
OBJ_ITEM_MAG_GAMEPRO=OBJ_ITEM_BASE+1
OBJ_ITEM_MAG_EGM=OBJ_ITEM_BASE+2
OBJ_ITEM_MAG_ANTIC=OBJ_ITEM_BASE+3
OBJ_ITEM_MAG_ANALOG=OBJ_ITEM_BASE+4
[...]
ItemNameNothing:
dc.b "Nothing",ITEMNAMEEND
ItemNameMagGamePro:
dc.b "GamePro",ITEMNAMEEND
ItemNameMagEGM:
dc.b "EGM",ITEMNAMEEND
ItemNameMagAntic:
dc.b "Antic",ITEMNAMEEND
ItemNameMagAnalog:
dc.b "Analog",ITEMNAMEEND
[...]
ItemNameTable:
;OBJ_ITEM_NOTHING
dc.l ItemNameNothing
;OBJ_ITEM_MAG_GAMEPRO
dc.l ItemNameMagGamePro
;OBJ_ITEM_MAG_EGM
dc.l ItemNameMagEGM
;OBJ_ITEM_MAG_ANTIC
dc.l ItemNameMagAntic
;OBJ_ITEM_MAG_ANALOG
dc.l ItemNameMagAnalog
We also need a list that will be referenced in ProcessDialog. I guess I should figure out how to store this in the scene definition or something. For now it's hard-coded in some random spot:
ItemListVBMagazine:
dc.w OBJ_ITEM_MAG_GAMEPRO
dc.w OBJ_ITEM_MAG_EGM
dc.w OBJ_ITEM_MAG_ANTIC
dc.w OBJ_ITEM_MAG_ANALOG
Here's some new text that will come up later. ITEMNAMESTART is a new constant that will be used to tell ProcessDialog to draw the next item in the list.
ItemMenu:
dc.b "{",ITEMNAMESTART
;-------------------------
; take menus and messages
;-------------------------
TakeMenuFull:
dc.b "You can't carry any",LF
dc.b "more items.",ETX
TakeMenuMagazines:
dc.b "Which magazine do you",LF
dc.b "want to take? ^",FF
dc.b "{",ITEMNAMESTART
TakeMagazinesOption0:
dc.b "You selected option 0.",ETX
TakeMagazinesOption1:
dc.b "You selected option 1.",ETX
TakeMagazinesOption2:
dc.b "You selected option 2.",ETX
TakeMagazinesOption3:
dc.b "You selected option 3.",ETX
ProcessDialog is doing the most complicated work, and it's not terribly complicated at all. When it encouters the ITEMNAMESTART character it looks up the name of the next item and sets it as the next text to draw. When it encounters the ITEMNAMEEND character it determines whether there are more items to draw. If so it resets the text back to ItemMenu, otherwise it moves to the next dialog state.
ProcessDialogTextDrawing:
[...]
TestItemNameStart:
cmpi.b #ITEMNAMESTART,d6 ; start of an item name?
bne.s TestItemNameEnd ; not the ITEMNAMESTART character, next test
;------------------------------------------
; build text for next item name in the list
;------------------------------------------
; lookup the object ID for the next item
move.l (MEM_DIALOG_ITEM_LIST),a6 ; copy address of item list to a6
move.w (MEM_DIALOG_ITEM_INDEX),d6 ; copy current item index to d6
mulu.w #WORD_SIZE,d6 ; multiply by WORD_SIZE to get the offset
adda.l d6,a6 ; add offset
move.w (a6),d6 ; copy item ID to d6
andi.w #$0FFF,d6 ; clear the base value
; object ID is in d6 now, find the address for the text
mulu.w #LWORD_SIZE,d6 ; multiply by LWORD_SIZE to get the offset
lea ItemNameTable,a6 ; point a6 to item name table
adda.l d6,a6 ; add offset
move.l (a6),(MEM_DIALOG_TEXT) ; copy value at a6 to MEM_DIALOG_TEXT
bra.w ExitProcessDialog ; exit
TestItemNameEnd:
cmpi.b #ITEMNAMEEND,d6 ; end of an item name?
bne.s TestLF ; not the ITEMNAMEEND character, next test
;---------------------------------------------
; determine if there are more items to display
;---------------------------------------------
addq #$1,(MEM_DIALOG_ITEM_INDEX) ; increment item name
cmpi.w #MAX_ITEMS,(MEM_DIALOG_ITEM_INDEX) ; at max items?
bge.s ProcessDialogTextEnd ; branch if at max items
; test if the next item is OBJ_ITEM_NOTHING
move.l (MEM_DIALOG_ITEM_LIST),a6 ; copy address of item list to a6
move.w (MEM_DIALOG_ITEM_INDEX),d6 ; copy current item index to d6
mulu.w #WORD_SIZE,d6 ; multiply by WORD_SIZE to get the offset
adda.l d6,a6 ; add offset
cmpi.w #OBJ_ITEM_NOTHING,(a6) ; test if value at a6 = OBJ_ITEM_NOTHING
beq.s ProcessDialogTextEnd ; branch if next item is OBJ_ITEM_NOTHING
cmpi.w #$0000,(a6) ; test if value at a6 = 0000
beq.s ProcessDialogTextEnd ; branch if next item is OBJ_ITEM_NOTHING
;----------------------------
; setup to draw the next item
;----------------------------
lea ItemMenu,a6 ; point a6 to the item menu text
move.l a6,(MEM_DIALOG_TEXT) ; copy a6 to MEM_DIALOG_TEXT
bsr.w SetItemMenuDrawLocation ; set the draw location of the next item
bra.w ExitProcessDialog
[...]
There's also some work to set where the next item name should be drawn in the dialog.
SetItemMenuDrawLocation:
move.w (MEM_DIALOG_ITEM_INDEX),d6 ; copy index of current item to d6
;test for 0001
cmpi.w #$0001,d6 ; is it one?
bne.s .1 ; branch if not
; move down 2 rows
move.l #(VDP_VRAM_WRITE_A+DIALOG_ROWCOL+$01020000),(MEM_DIALOG_VPD)
bra.s ExitSetItemMenuDrawLocation ; exit
.1 ; test for 0002
cmpi.w #$0002,d6 ; is it two?
bne.s .2 ; branch if not
; move down 1 row
move.l #(VDP_VRAM_WRITE_A+DIALOG_ROWCOL+$00980000),(MEM_DIALOG_VPD)
bra.s ExitSetItemMenuDrawLocation ; exit
.2 ; test for 0003
cmpi.w #$0003,d6 ; is it theee?
bne.s ExitSetItemMenuDrawLocation ; exit if not
; move down 1 row and column
move.l #(VDP_VRAM_WRITE_A+DIALOG_ROWCOL+$01180000),(MEM_DIALOG_VPD)
ExitSetItemMenuDrawLocation:
rts
Moving the selector sprite needs to account for the new four choice style. It also needs to account for the possibility that there might only be 1-3 items in the list.
FourChoiceLeftRight:
btst.l #DIALOG_FLAG_STYLE_ITEM_MENU,d7 ; test if this is an item menu
beq.s .1 ; not an item menu, branch
; MEM_DIALOG_ITEM_INDEX has the total number of items in dialog +1
cmpi.w #$0002,(MEM_DIALOG_ITEM_INDEX) ; are there 2 or fewer items?
ble.s ExitFourChoiceLeftRight ; exit if 2 or fewer
; there are 3 or 4 items, 4 items is fine
cmpi.w #$0004,(MEM_DIALOG_ITEM_INDEX) ; are there 4 items?
bge.s .1 ; if so continue
; three items is the only possibility now
cmpi.w #$0001,(MEM_MENU_SELECTION) ; is the 2nd item selected?
beq.s ExitFourChoiceLeftRight ; if so exit
.1
cmpi.w #$0002,(MEM_MENU_SELECTION) ; test if <2
blt.s .2 ; MEM_MENU_SELECTION is > 2
subq #$0002,(MEM_MENU_SELECTION) ; subtract 2 to rollover
bra.s ExitFourChoiceLeftRight ; exit
.2
addq #$0002,(MEM_MENU_SELECTION) ; add 2 to move right
ExitFourChoiceLeftRight:
rts
FourChoiceUpDown:
btst.l #DIALOG_FLAG_STYLE_ITEM_MENU,d7 ; test if this is an item menu
beq.s .1 ; not an item menu, branch
; MEM_DIALOG_ITEM_INDEX has the total number of items in dialog +1
cmpi.w #$0001,(MEM_DIALOG_ITEM_INDEX) ; is there only 1 item?
beq.s ExitFourChoiceFourChoiceUpDown ; exit if only 1 item
; there are 3 or 4 items, 4 items is fine
cmpi.w #$0004,(MEM_DIALOG_ITEM_INDEX) ; are there 4 items?
bge.s .1 ; if so continue
; three items is the only possibility now
cmpi.w #$0002,(MEM_MENU_SELECTION) ; is the 3rd item selected?
beq.s ExitFourChoiceFourChoiceUpDown ; if so exit
.1
cmpi.w #$0000,(MEM_MENU_SELECTION) ; test if 0
beq.s FourChoiceUpDownAdd ; MEM_MENU_SELECTION is 0
cmpi.w #$0002,(MEM_MENU_SELECTION) ; test if 2
beq.s FourChoiceUpDownAdd ; MEM_MENU_SELECTION is 2
subq #$0001,(MEM_MENU_SELECTION) ; subtract 1 to rollover
bra.s ExitFourChoiceFourChoiceUpDown ; exit
FourChoiceUpDownAdd:
addq #$0001,(MEM_MENU_SELECTION) ; add 2 to move down
ExitFourChoiceFourChoiceUpDown:
rts
When the player confirms a menu selection there's a little new work to determine if this is an item list and if so to store the selected item.
ConfirmMenuSelection:
[...]
ConfirmMenuSelection4Choice:
move.w (MEM_MENU_SELECTION),(MEM_MENU_RESPONSE) ; selection->response
btst.l #DIALOG_FLAG_STYLE_ITEM_MENU,d7 ; test if this is an item menu
beq.s ExitConfirmMenuSelection ; not an item menu, branch
; store selected item in MEM_ACTIVE_ITEM
move.l (MEM_DIALOG_ITEM_LIST),a6 ; copy address of item list to a6
move.w (MEM_MENU_SELECTION),d6 ; copy current item index to d6
mulu.w #WORD_SIZE,d6 ; multiply by WORD_SIZE to get the offset
adda.l d6,a6 ; add offset
move.w (a6),(MEM_ACTIVE_ITEM) ; copy item ID to d6
[...]
The end result is this little dialog showing a list of items:
That was fun, OK not a lot of fun to debug but overall this wasn't too bad. Let's wrap this up by...
Responding to item selections
The ProcessAction subroutine was previously built to handle forwarding actions to the appropriate day/scene/action handler. One (of possibly many) flaws with this approach is there are some actions that don't change based on the day & scene. Giving an item is one of them, or at least the initial act of displaying the list of items to give. There needs to be a default handler for this, it needs to be smart enough to determine if the player is trying to open the Give menu or has just selected an item in it. That's pretty easy to sort out:
ProcessAction:
bsr.w ResetDialog ; reset the dialog
; ----------------------------------------
; test if a default handler should be used
; ----------------------------------------
cmpi.w #ACTION_TAKE_GIVE,(MEM_ACTION_ID) ; test if this is a give action
bne.s ProcessActionBuildActionTableOffset ; not give action, branch
move.w (MEM_ACTION_TARGET_OBJID),d7 ; copy action target to d7
andi.w #OBJ_NPC_BASE,d7 ; and against base npc ID
beq.s .1 ; target is not an NPC
; --------------------------------------------
; default handler for giving an item to an NPC
; --------------------------------------------
cmpi.w #$0000,(MEM_ACTIVE_ITEM) ; test if there is an active item
bne.s ProcessActionBuildActionTableOffset ; responding to a give menu
bsr.w DefaultActionGive ; otherwise launch default action
bra.s ExitProcessAction ; exit
.1 ; test if target is scenery
move.w (MEM_ACTION_TARGET_OBJID),d7 ; copy action target to d7
andi.w #OBJ_SCENE_BASE,d7 ; and against base npc ID
beq.s ProcessActionBuildActionTableOffset ; target is not scenery
cmpi.w #MAX_ITEMS,(MEM_PLAYER_ITEM_COUNT) ; carrying max items?
bne.s ProcessActionBuildActionTableOffset ; branch if < max
bsr.w DefaultActionInventoryFull ; launch default action
bra.s ExitProcessAction ; exit
ProcessActionBuildActionTableOffset:
bsr.w BuildActionTableOffset ; build action table offset
lea ActionTable,a5 ; point to action table
adda.w (MEM_ACTION_TABLE_OFFSET),a5 ; move to offset location
move.l (a5),a6 ; a5 has the address of the subroutine to jump to
jsr (a6) ; jump to location of code to process this event
ExitProcessAction:
move.l (MEM_GAME_STATE),d7 ; copy current game state to d7
bclr.l #STATE_FLAG_ACTION,d7 ; clear action flag
move.l d7,(MEM_GAME_STATE) ; save it back
rts
The default Give action then displays the menu or a message saying the player doesn't have any items. As a bonus, there's also a message if a player tries to take an item when their inventory is full.
;--------------------------------------------------------------------
; builds the give menu or displays message if the player has no items
;--------------------------------------------------------------------
DefaultActionGive:
cmpi.w #$0000,(MEM_PLAYER_ITEM_COUNT) ; is item count 0?
beq.s DefaultActionGiveNoItems ; branch if 0
lea ItemMenu,a6 ; load dialog text
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
lea MEM_PLAYER_ITEMS,a6 ; load item list
move.l a6,MEM_DIALOG_ITEM_LIST ; copy address to MEM_DIALOG_ITEM_LIST
move.l (MEM_DIALOG_FLAGS),d7 ; copy dialog flags to d7
bset.l #DIALOG_FLAG_STYLE_TEXT_4CHOICE,d7 ; set text choice flag
bset.l #DIALOG_FLAG_STYLE_ITEM_MENU,d7 ; set item menu flag
move.l d7,(MEM_DIALOG_FLAGS) ; save updated dialog flags
bra.s ExitDefaultActionGive ; exit
DefaultActionGiveNoItems:
lea DialogTextNoItems,a6 ; load dialog text
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
ExitDefaultActionGive:
rts
;-------------------------------------------------------
; displays a message if the player can't hold more items
;-------------------------------------------------------
DefaultActionInventoryFull:
lea TakeMenuFull,a6 ; load dialog text
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
ExitDefaultActionInventoryFull:
rts
In the action script there are two ways we can respond to a Give/Take action. We can look at the selected index in the dialog and display a message. This code example does that. Another option is responding based on the item ID that was selected. This is more useful in 99% of cases but I coded the other one first (and again am lazy). This also shows how to handle giving an item to an NPC.
Day00Scene00Action02: ; ACTION_TAKE_GIVE
move.w (MEM_ACTION_TARGET_OBJID),d6 ; copy action target to d6
andi.w #OBJ_SCENE_BASE,d6 ; and against base scene ID
beq.w Day00Scene00Action02DefaultGive ; not scenery
move.w (MEM_ACTION_TARGET_OBJID),d6 ; copy action target to d6
cmpi.w #OBJ_SCENE_VB_MAGS,d6 ; is the magazine rack the target?
bne.w Day00Scene00Action02DefaultTake ; not the magazine rack, branch
; check if we are coming back from a take menu or need to open one
cmpi.w #DIALOG_NO_RESPONSE,(MEM_MENU_RESPONSE) ; test response
beq.s .5 ; branch if no response
cmpi.w #$0000,(MEM_MENU_RESPONSE) ; test response
bne.s .1
lea TakeMagazinesOption0,a6 ; load dialog text
bra.s .4
.1
cmpi.w #$0001,(MEM_MENU_RESPONSE) ; test response
bne.s .2
lea TakeMagazinesOption1,a6 ; load dialog text
bra.s .4
.2
cmpi.w #$0002,(MEM_MENU_RESPONSE) ; test response
bne.s .3
lea TakeMagazinesOption2,a6 ; load dialog text
bra.s .4
.3
cmpi.w #$0003,(MEM_MENU_RESPONSE) ; test response
bne.s .3
lea TakeMagazinesOption3,a6 ; load dialog text
.4
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
move.l (MEM_DIALOG_FLAGS),d7 ; copy dialog flags to d7
bclr.l #DIALOG_FLAG_STYLE_TEXT_4CHOICE,d7 ; set text choice flag
move.l d7,(MEM_DIALOG_FLAGS) ; save updated dialog flags
bra.s ExitDay00Scene00Action02
.5
lea TakeMenuMagazines,a6 ; load dialog text
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
lea ItemListVBMagazine,a6 ; load item list
move.l a6,MEM_DIALOG_ITEM_LIST ; copy address to MEM_DIALOG_ITEM_LIST
move.l (MEM_DIALOG_FLAGS),d7 ; copy dialog flags to d7
bset.l #DIALOG_FLAG_STYLE_TEXT_4CHOICE,d7 ; set text choice flag
bset.l #DIALOG_FLAG_STYLE_ITEM_MENU,d7 ; set item menu flag
move.l d7,(MEM_DIALOG_FLAGS) ; save updated dialog flags
bra.s ExitDay00Scene00Action02
Day00Scene00Action02DefaultTake:
lea DialogTextCantTake,a6 ; load dialog text
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
bra.s ExitDay00Scene00Action02
Day00Scene00Action02DefaultGive:
lea DialogTextNothingHappens,a6 ; load dialog text
move.l a6,MEM_DIALOG_TEXT ; copy address to MEM_DIALOG_TEXT
ExitDay00Scene00Action02:
rts
And the end result is this exciting message:
With just a little work items can be added to all the shelves and racks. We can also have the NPCs respond to items you show them. This is all part of the overall game plan for a one-room demo. This seems like a good enough time as any to check in on that...
What's next?
Looking at the "plan" from three articles ago:
1) Dialog between characters - meaning an NPC says something to you and you respond. Maybe you respond with more dialog, maybe you give them an item. That leads to...
2) Inventory management - along with basic stuff like taking and giving items.
3) Game event tracking - we need a way to say "if [X] happened and you talk to character [Y] then do [Z]", I'll probably over-complicate this. I won't call this 100% complete but it's functional enough.
4) Scripted sprite movements - if the ultimate goal is to get a customer to leave a store then there needs to be an animation where they walk away.
5) Adding & removing NPCs from the current scene - the one NPC is hard-coded, there needs to be a way to move NPCs in and out of scenes. The removing part hasn't been needed yet but it's small and will be addressed with #4.
6) To make this look like a real demo I should really create a title screen and ending message.
7) Dozens of things I didn't think of that will all be painful to work out.
Next round will be scripted sprite movements, along with removing NPCs from the scene. Of course adding more items and NPC dialog needs to happen along the way too.
Download
Download the latest source code on GitHub
Related