Learn Python in 30 Days — Day 30 – Final Project Build (Part 3): Finishing the Game
Day 30 – Finishing the Game
This is the final day of the series, and the last build step of your open-world text RPG: RetroRealm.
All example files for this series are available on my GitHub: Learn-Python-in-30-Days
By now your project has:
- A full world made of multiple 10×10 regions
- Blocking movement rules
- Region-to-region travel
- Danger-level encounters
- A working turn-based combat loop
- A modular Python engine
Today, we finish everything else, the pieces that turn RetroRealm from a simple overworld explorer into a real, living RPG world.
By the end of Day 30 you’ll have:
- Fully explorable villages
- NPCs you can talk to
- Shops to buy and sell items
- Magic, spells, and usable items in combat
- A levelling system
- Saving & loading your progress
- A complete, fully playable RPG engine
Let’s complete this game.
Village Engine
Villages came from villages.json on Day 28, as part of the village generator.
Up to now, they’ve just been data: names, maps, NPCs, and shops, all stored in JSON.
Today, we add a village navigation system with:
- Walking inside the village
- NPC tiles
- Shop tiles
- Exit tiles
- Conversations and shopping
- A robust loader that works with different
villages.jsonformats (dict or list,world_x/world_y, olderx/y, orcoords)
First create the file: village_engine.py
We reuse the same direction helper that was wrote earlier so villages and the overworld share a consistent input style (n/s/e/w → north/south/east/west).
The village lookup function
This function is the “glue” between the overworld and the village data. It takes the player’s current region and coordinates, and finds the matching village.
Here we construct a "x,y" string so we can support older formats that stored coordinates as "0,0" keys.
This handles the case where villages is a dictionary of regions, and each region contains a dictionary keyed by "x,y".
This part supports your current format (world_x/world_y).
The big win here: if you tweak your village generator later, this function is forgiving and your game doesn’t suddenly break.
Entering and navigating a village
This pulls the player’s overworld position and uses find_village_at() to fetch the matching village. If no village exists at that location, the command safely does nothing.
Inside the village we track a separate position: px, py.
This is the player’s location inside the village’s own mini-map grid.
Each tile has a type such as "floor", "npc", "shop", or "exit". Printing this helps the player understand what they’re standing on.
Depending on the tile type, we show available actions.
Village commands are handled in a little REPL, separate from the overworld loop.
Movement inside the village
Village movement mirrors overworld movement:
arrow-style commands get converted to directions, then to tile indices, with bounds checking.
Exiting the village and interacting with tiles
This cleanly returns to the overworld loop in main.py.
If you’re standing on an NPC tile and you type talk, we delegate to handle_npc().
If you’re standing on a shop tile and type shop, we open the shop menu.
Any other command is rejected. However you can easily add other commands of your own, say to interact at Inns or Farms
NPC conversations
NPCs are defined in your JSON data, and this function simply prints their name and dialogue.
It’s intentionally simple so you can later expand it into branching conversations or quests if you want.
Shop system
Shops are the heart of the in-game economy. Players can buy items, inspect their inventory, and leave at any time.
We list items with a number, so the player can buy via numeric input.
leave exits the shop and returns the player to the village loop.
inv shows the player’s current inventory. This lets them see what they already own before buying more.
Numeric input means “buy item number X”:
- Checks index validity
- Checks if the player has enough gold
- Deducts gold
- Adds the item name to
player.inventory
This shop system ties directly into combat, where those items become usable.
Magic, Items & Levelling in Combat
Now we expand combat to support:
- Magic attacks
- Consumable items
- Levelling
- Better damage scaling
This turns combat from “attack spam” into something much more RPG-like:
you’ll manage resources, choose between physical attacks, magic, and healing, and enjoy a sense of progression from levelling.
Update combat.py to the final version:
combat.py
We still use random for damage variation.
We clone the creature so we don’t mutate the original template. Each battle gets its own HP pool.
The combat loop
We display the current HP/MP and prompt the player to choose between:
a – physical attackm – magici – itemr – run
Physical attack
Damage is a small random value plus the player’s level, so levelling up directly improves basic attacks.
Magic attack
Magic:
- Costs 3 MP
- Deals more damage than a regular attack
- Also scales with level
This introduces a risk/reward decision: do you spend MP now for burst damage, or save it for later?
Items in combat
If the inventory is empty, the action is blocked.
If not, the player chooses an item by number.
We:
- Remove the selected item from inventory
- Apply a simple rule: if the word
"potion"is in the item name, heal 10 HP
This is intentionally simple, but easy to extend. You could later add different item types or effects by checking other keywords.
Running away
Running away immediately ends combat and returns to the overworld. No XP, no gold.
Handling invalid actions
Just a guard for typos or unsupported input.
Enemy turn
If the enemy is still alive after the player’s turn, it hits back using its attack value from your creature data. This keeps the fight turn-based and predictable.
Combat resolution & levelling
If the player reaches 0 HP, the game exits.
On a win, you:
- Gain XP and gold from the creature
- Update the player’s stats
- Call
check_level_up()to see if you levelled
Every level requires level * 20 XP.
When you cross that threshold, you:
- Increase level
- Add 10 to health
- Add 5 to magic
This makes your character meaningfully stronger over time.
Save & Load System
RPGs are meant to be played over multiple sessions.
So now we add a simple but powerful save and load system.
Create: the file: save_load.py
player.__dict__ contains all the attributes of the player object:
- Location
- Level
- XP
- HP / MP
- Gold
- Inventory
By dumping this dictionary, we capture the entire player state in one go.
load_game reverses the process:
-
Opens
save.json - Iterates over saved attributes
- Sets them back on the existing player object
If no save file exists, it prints a friendly message instead of crashing.
This version saves the whole Player object by dumping player.__dict__ and restores it on load.
Updated main.py
Your game loop now supports:
- Villages
- Saving & loading
- Correct imports
- Working data paths via
world_data.py(data/world.json,data/villages.json,data/creatures.json)
This file is the “director” of your game: it ties all the subsystems together.
We import:
Player for the main character- World, village, and creature loaders
- Map description and movement
- Encounter logic
- Combat
- Village engine
- Save/load system
start_game():
-
Loads the data
-
Creates the player
-
Prints a splash screen
-
Drops into the main game loop
The main game loop
Each turn:
- Describe the player’s current location
- Show available commands
- Ask what they want to do
Allows the player to quit gracefully.
stats prints a simple summary of the player’s current build.
If the player is standing on a village tile, enter_village will handle the interior navigation.
These delegate to the save/load system you just built.
Movement:
- Moves the player according to overworld rules
- Checks the location’s danger level
- Possibly triggers combat via
maybe_trigger_encounter
Fallback for invalid input.
Standard Python entry point, so running python main.py launches the game.
What You Built on Day 30
RetroRealm is now a complete open-world text RPG!
All example files for this game are available at Day 30 Final Game
Today you added:
Villages, Walkable mini-maps with NPCs, shops, and exits.
NPC Conversations, Simple, readable dialogue loaded from data.
Item Shops, Buy items, check your inventory, spend gold.
Magic, MP-based spellcasting usable in combat.
Usable Items, Potions and consumables that modify player stats.
Levelling System, XP thresholds, stat upgrades, and character progression.
Save/Load, Quit any time and continue where you left off.
Full Game Loop, Movement → encounters → villages → shopping → combat → progression.
The End
You now have the skills to start build anything in Python, games, tools, utilities, apps, automation, web scripts, and more. From here you can pick up more advanced techniques and improve your programming skills further.

Comments
Post a Comment