Coding up the Buildings

Lacuna • August 17, 2022

Well this was a very fun process, full of pitfalls. My initial idea was to have a set of locations or "plots" that the player could create new buildings. Each building would have certain requirements on resources, and possibly other buildings and the amount of plots would be controlled by the level of your command center or main building.

No this is all fine but of course I ran into problems when looking at what happens when a building is upgraded. Since I'm not using crons for any of the game I have to keep track of when a building is upgraded so I can update the resources accordingly. Equally, when a building is downgraded; a feature that I may allow players to do, or may happen during conflict; then we need to track the new resource and storage values.

Initially I considered just updated the resources by the time difference since we last visited the page times the production rate, but this doesn't work as building may have been completed during your time away which could change the production/consumption rate, or the storage capacity.

So, the solution is to track the time we last visited, then for each building that is completed since that time, update the resources for all buildings up to that time, upgrade the building's properties to suit, and continue:

If you consider the global variables:

last_visited       - the date/time we last visited this world)
global_production  - the resource production per hour at the time we last visited
global_consumption - the resource consumption per hour at the time we last visited
global_storage     - the storage capacity at the time we last visited
global_resource    - the quantity of the resource at the time we last visited

and building have:

id          - building ID
production  - the base production per hour
consumption - the base consumption per hour
storage     - the storage capacity

finally, plots would be constructed:

building_id - points to a specific building
level       - the current building level
state       - "building", "demolishing" or "completed"
completed   - date/time this building was last completed

Our pseudo-code becomes;

for each plot where plot.building.state != completd and completed < last_visited:
    time_difference = plot.building.completed - last_visited

    for each plot:
        global_resource += (plot.building.production - plot.building.consumption) / (time_difference)

    if plot.building.state == "building":
        plot.level += 1
    else:
        plot.level -= 1

    update_global_production__comsumption_and_storag_for_new_building_level()

    last_visited = plot.building.completed

So now, all buildings will be complete be will be completed some-time on the future, so we can simply:

time_difference = now - last_visited
for each plot:
    global_resource += (plot.building.production - plot.building.consumption) / (time_difference)
last_vsited = now

Obviously, I've missed out storage limitations, multiple resources, removal of buildings after demolitio, but those are all simple enough.

Updating global production/consumption and storage values is simple enough if you use a simple formula for each building's rate of production/consumption/storage as discussed earlier in this blog.

Implementing things like resource theft is fairly simple to drop in and may be a cleaner concept rather than buildings being damaged due to conflict.

Wrapping up

Keeping track of resources accurately without crons was my goal here and I believe this is now addressed. Initial attempts at actually writing this in code proved messy, but there a lot of repeating patterns, so careful extraction of methods yields a rather nice bit of code that runs very fast, fast enough that the user is not aware of it when visiting a page.

By using a simple bit of javascript to update resource levels each second purely on the current page allow us to show the current resource levels to the user without actually touching the database. It's wise to either refresh the page or make an AJAX call to handle building upgrades/downgrades if the completion time for any plot is reached when viewing a page, but again, that's a simple enough task.