Harvest Nothing 64 / Event system revamp & inventory

This is the first post on a long succession to come about my progress on this project.
Simple recall: I’m replicating Natsume’s HarvestMoon64 [HM64] for educational purpose only. The goal for me is to learn how to use GameMaker:Studio [GM:S] with GML.

Today I discuss the work I’ve done this week, modifying / cleaning event system and implementing inventory system.

Event system

I created an event system overlay on top of GM:S built-in event_user() function (introduced in this post). Obviously there was room for improvement and factorizing so I started by doing this.

Lazy loading ds_list through different objects

I tried lazy loading ds_list of events’ subscribers in s_SubscribeTo:

if(!ds_exists(EventManagerID.ListVariableName, ds_type_list)){
  EventManagerID.ListVariableName = ds_list_create();

But GM:S seems to get in troubles when it comes to create ds_list in instance variable already initialized to undefined in an other object. Maybe a with structure would solve this problem. Instead I just created ds_list right from the start in EventManager object, later in the project I’ll come back to this problem.

Asynchronous event processing

One major improvement is the implementation of an event queue using a ring buffer to store events that happened and process one event each room step.
This pile of scripts moves the ring buffer:

  • s_EventQueueInit(): Initialize an instance array of “maximum events pending at the same time” size to undefined, an instance integer Head to 0 and one other for Tail.
    It’s called by o_Events at creation (Before game world creation).
  • s_EventProcess(): Test Head equality with Tail, if true return immediately. Else it notifies subscribers of the event in the Head cell of the array, then advance Head to the next cell.
    It’s called by o_Events once each room step.
  • s_EventPublish(enum g_Events): Add the event given in the Tail cell of the array, then advance the Tail to the next cell. It’s called by every objects that wish to publish an event.

Inventory system

If you’re reading this, you’re probably using MS Edge, there is a gif that is hidden right here. Edge is literally killing itself on it, I’m sorry for that inconvenience and will look for a solution for future posts. Nevertheless if you still want to see this gif with Edge, open dev tools (F12) and modify properties of it: width=600 / height=338 / style=”visibility:visible”

Starting from the end, here’s what the inventory screen currently looks like. Fancy graphics will come later in the project.

Demonstration of inventory system

Comparison of the HM64’s inventory screen with the one I made.
There is no visual differences between zones for the moment and no arrow to indicate that page can be turned, these are things I’ll add later.

Inventory comparison between HarvestMoon64 & HarvestNothing64

General behavior

This system is compounded by multiple objects with limited responsibilities.

  • o_Inventory is the entry point, it’s created at the start of the game and spawns o_InventoryData that hold… data.
  • o_InventoryScreen is created at the inventory overture and destroyed at the close, it’s the central pivot of the graphical representation of the inventory.
    It spawns grids of o_InventorySlot and dispose them properly on the screen (Slots are just the frame, they don’t draw the objects inside them).
    It also spawn the o_InventoryCursor which is just aware of its coordinates on the grid & the screen but don’t knows what it’s pointing at.

The screen is completely independent from the data and don’t intervene on data’s operations.

Inventory system objects

Data modelisation

The data are simply stored in one dimensional arrays, there’s one array for each of 3 categories of objects: Tools / Belongings / Keys. Additionally each hands has one dedicated integer (the equipped tool and belonging).
The value stored is the object_index, an integer defined by the position of the object in the project’s hierarchy under “Objects” folder.
This way all operations are done on positive integers rather than strings, and don’t rely on instances which would be lost between rooms and occupy memory for nothing.
Another advantage is that I just have to create one object for each one I want to include in the game (not two separate versions, one for the inventory and another for the game).

One design flaw though is based on this inevitable choice:

  • Should the object knows in which category it belongs to?
  • Should the inventory keeps an array for each category to compare to?

I decided to go with the first option, thus avoiding search through arrays when adding an object to the inventory in game and avoiding hard-written lists of object indexes which is less maintainable and more error-prone.

Inventory screen

When the player open the inventory screen (pressing a dedicated key), the context changes and the arrow keys immediately control the cursor. He can select objects and swap them with others using ‘Validate’ key and cancel the current selection or close the screen with the ‘Cancel’ key.

The inventory screen is cut in zones corresponding to different types of objects and the cursor moves differently between them wether something is selected or not.

Inventory zones

For example when a belonging (2) is selected, if you press up arrow key on the first line of the zone, the cursor will jump directly to the off hand (5) and can’t go anywhere else than these 2 zones.
The keys’ zone (3) has multiple pages that can be turn when going up or down on edges.

The screen is another room you’re shifting to when opening the inventory, the grids are created and disposed at this moment: the room in GM:S has just one instance of o_InventoryScreen in it.
The hands’ slots are not part of the inventory screen sub-system, they are permanent elements of the top GUI and therefore have dedicated operations to keep them updated according to o_InventoryData.

GML flaw

As GML is not fully documented, you have to run into certain edge cases to know that they exists. One recurrent problem though is the “constant” key words’ ambiguity, depending on context they change their value / meaning including “the constant” undefined. (From the GML manual: “the constant undefined”…)
Here are two examples I ran into last week.

  1. with(undefined)

If you happen to get into this situation, GM:S will take the first object in your editor ‘Objects’ hierarchy and use it inside the block. There is no true boolean type in GML, true and false are constants defined to 1 and 0.

What’s the link between boolean and with(undefined)?
with expects an instance id or an object_index, not a boolean. At runtime it evaluates the expression between brackets to get this information, in doing so I suspect it to test especially for undefined and return false rather than throwing an exception.

I doubt with to be defined in a high level assembler but I haven’t verified it nor have I the knowledge to do so, if that’s the case I’m probably absolutely wrong about the cause of this behavior.

Nevertheless, 0 is returned as result of the evaluation and it’s considered being an object_index therefore used to find the first instance in room of this object and use it.
This is an extract from my script updating the GUI OffHand slot before adding is_undefined():

//Is there an object displayed? Is it different from the data?
if(i_Object != l_Data.i_OffHand){
  with(i_Object){ //If you don't prevent undefined value, it'll be evaluated as 0
    instance_destroy(); //Destroy that object
  i_Object = undefined; //Clean the reference to its instance

  2. object’s name ambiguity

This one is me whining about a useful/messy feature rather than a real problem.
(And being a bit hypocritical because sometimes it facilitated me the task at hand ;p)

When you call an object by its name you get its index in your editor hierarchy, at core the value of the object’s name never change when evaluating it. But his meaning change depending on context, it’s disturbing when you’re used to traditional object oriented, strongly typed, high level languages.
In C++ it would be like using the Class name (the object’s name) as a pointer to objects instances.

Consider we have an object o_Object with an instance variable i_Variable and we’re gonna use it in different situations:

var l_MyObject = o_Object; //o_Object is an object_index
var l_MyInstance = o_Object.i_Variable; //o_Object is the first instance ID
with(o_Object) //o_Object is an array of instance IDs

Next update

Now I’ll work on something more “concrete”: snap the player’s movement to the isometric grid and propers collisions’ detection.
See you on the next post! :)