Homunculus Devlog 2018-04-21
In this post:
- I want Single Responsibility, not, like, a billion.
Before I mess with the code, I want to document all of the responsibilities it has.
The code is accessed through a console entry point. The main function:
- To create the game object:
- Reads the save file
- Loads data if applicable
- Skips over the error if the file does not exist
- Creates a new game if it didn't load one
- To set up the game window:
- Loads the "arial10x10" font from the resources directory
- Creates a console using the values specified in the constants module
- Creates sub-consoles
- To play the game:
- Calls the play_game function with the above arguments
The play_game function:
- To set up the game loop:
- Sets the fov_recompute, mouse_coordinates, previous_game_state, and targeting_item variables
- To run the game loop (buckle up, this is going to be a long ride):
- Conditionally recomputes the fov
- Renders everything
- Flushes the console
- Clears the console
- Sets fov_recompute to False
- Iterates over the event queue until it finds an event it recognizes
- Skips the rest of the loop if it didn't find an event it recognized
- Converts the user input into an action dictionary
- Unpacks various flags from the action dictionary
- Creates a list of action results
- Extends the list:
- If a move action exists:
- Do map collision detection
- If no collisions:
- Attempt to initiate combat
- If targets, add the combat result to the action results
- If no targets, move
- Set the game_state to ENEMY_TURN (this is the only code in this part of the function that does this?)
- Else if a pickup action exists:
- Iterate through the items until we find one under the player
- Add a pickup result to the action results
- If a show_inventory action exists, use previous_game_state to temporarily enter use mode
- If a drop_inventory action exists, use previous_game_state to temporarily enter drop mode
- If the inventory_index value is defined, and the player is not dead, and the index is in range, add the appropriate result to the action results
- Handle targeting actions:
- Left click to attempt to use the item at the mouse coordinates; if successful, add the results to the action results
- Right click to add a targeting_cancelled result to the action results
- If an exit action exists:
- In an inventory mode, switch to the previous game state
- In targeting mode, cancel targeting as above
- In all other cases, close the game:
- If the player is dead, delete the save file
- Otherwise, save over the save file
- Return
- If a fullscreen action exists, toggle fullscreen
- If a move action exists:
- Process the action results; for each result:
- Extract various possible values
- Put messages in the message log
- Process deaths, add the death message to the message log
- Process item pickups, switch to ENEMY_TURN
- Process item uses, switch to ENEMY_TURN
- Process item drops, switch to ENEMY_TURN
- If targeting, use previous_game_state to switch to targeting mode, add the targeting message to the message log
- If targeting_cancelled, restore the game state, and add the cancel message to the message log
- If it's the ENEMY_TURN, iterate through all entities:
- If it has an AI component, take the AI's turn and get the results
- To process the results:
- Extract message and dead_entity variables
- Put messages in the message log
- Process dead entities:
- If it's the player, put the message from the kill_player function in the message log, and set the game state to PLAYER_DEAD
- Otherwise, put the message from the kill_monster function in the message log
- Break out of the inner two loops if the player is dead
- If the player is not dead, set the game state to PLAYERS_TURN
The way I want to cope with this is to turn the collection of variables that represent the game state into a cohesive object, and have the game loop coordinate between different methods:
- conditional_fov_recompute
- (process any render events from the previous iteration)
- render
- pipeline:
- on the player's turn:
- input events to actions
- actions to actions results
- action results to messages
- on the enemy turn:
- action results to messages
- on the player's turn:
Ideally later, the details of rendering get pulled out into another function, and the event loop then pipelines messages into abstract render events, then yields render events to a concrete render process. So at a high level, the loop would go: apply render events, render with the renderer, populate render events from the game logic.
Next week, I try to execute on the above plan. Also, I should look at the rest of the code, but the main() function is distractingly huge.