Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions data/buildings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{"rect": [200, 150, 200, 120], "name": "Home", "type": "home"},
{"rect": [600, 300, 180, 240], "name": "Office", "type": "job"},
{"rect": [1100, 700, 300, 100], "name": "Shop", "type": "shop"},
{"rect": [400, 900, 160, 180], "name": "Park", "type": "park"},
{"rect": [460, 960, 80, 80], "name": "Deal Spot", "type": "dealer"},
{"rect": [580, 960, 80, 80], "name": "Deal Spot", "type": "dealer"},
{"rect": [900, 450, 220, 160], "name": "Gym", "type": "gym"},
{"rect": [1200, 250, 200, 160], "name": "Library", "type": "library"},
{"rect": [300, 600, 180, 160], "name": "Clinic", "type": "clinic"},
{"rect": [800, 750, 200, 150], "name": "Bar", "type": "bar"},
{"rect": [1400, 950, 160, 120], "name": "Alley", "type": "dungeon"},
{"rect": [1300, 550, 180, 150], "name": "Pet Shop", "type": "petshop"},
{"rect": [1000, 100, 200, 140], "name": "Bank", "type": "bank"},
{"rect": [400, 200, 200, 150], "name": "Town Hall", "type": "townhall"},
{"rect": [150, 400, 200, 150], "name": "Workshop", "type": "workshop"},
{"rect": [100, 1050, 220, 120], "name": "Farm", "type": "farm"},
{"rect": [1500, 400, 80, 100], "name": "Woods", "type": "forest"},
{"rect": [1800, 350, 240, 180], "name": "Mall", "type": "mall"},
{"rect": [2100, 150, 300, 200], "name": "Suburbs", "type": "suburbs"},
{"rect": [2400, 900, 260, 140], "name": "Beach", "type": "beach"},
{"rect": [1900, 800, 220, 120], "name": "Stall", "type": "business"},
{"rect": [2900, 500, 220, 160], "name": "Tower", "type": "boss"}
]
9 changes: 9 additions & 0 deletions data/quests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[
{"description": "Earn $200", "next_index": null},
{"description": "Reach STR 5", "next_index": null},
{"description": "Reach INT 5", "next_index": null},
{"description": "Earn $300", "next_index": 1},
{"description": "Reach STR 5", "next_index": 2},
{"description": "Defeat 3 alley thugs", "next_index": 3},
{"description": "Own every home upgrade", "next_index": null}
]
5 changes: 5 additions & 0 deletions data/sidequests.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{"name": "Bank Delivery", "description": "Deliver paperwork from the Bank to the Library", "target": "library", "reward": 50},
{"name": "Courier Errand", "description": "Deliver a package from Sam to the Gym", "target": "gym", "reward": 30},
{"name": "Beach Delivery", "description": "Pick up sunglasses from the Mall and bring them to the Beach", "target": "beach", "reward": 40}
]
28 changes: 2 additions & 26 deletions game.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
gain_crafting_exp,
)
from businesses import BUSINESSES, buy_business, manage_business
from loaders import load_buildings
from combat import (
energy_cost,
fight_brawler,
Expand Down Expand Up @@ -163,32 +164,7 @@
SOUND_ENABLED = False
print("Audio disabled: mixer could not initialize")

BUILDINGS = [
Building(pygame.Rect(200, 150, 200, 120), "Home", "home"),
Building(pygame.Rect(600, 300, 180, 240), "Office", "job"),
Building(pygame.Rect(1100, 700, 300, 100), "Shop", "shop"),
Building(pygame.Rect(400, 900, 160, 180), "Park", "park"),
Building(pygame.Rect(460, 960, 80, 80), "Deal Spot", "dealer"),
# Move the deal spot just outside the park so the player can reach it
Building(pygame.Rect(580, 960, 80, 80), "Deal Spot", "dealer"),
Building(pygame.Rect(900, 450, 220, 160), "Gym", "gym"),
Building(pygame.Rect(1200, 250, 200, 160), "Library", "library"),
Building(pygame.Rect(300, 600, 180, 160), "Clinic", "clinic"),
Building(pygame.Rect(800, 750, 200, 150), "Bar", "bar"),
Building(pygame.Rect(1400, 950, 160, 120), "Alley", "dungeon"),
Building(pygame.Rect(1300, 550, 180, 150), "Pet Shop", "petshop"),
Building(pygame.Rect(1000, 100, 200, 140), "Bank", "bank"),
Building(pygame.Rect(400, 200, 200, 150), "Town Hall", "townhall"),
Building(pygame.Rect(150, 400, 200, 150), "Workshop", "workshop"),
Building(pygame.Rect(100, 1050, 220, 120), "Farm", "farm"),
Building(pygame.Rect(1500, 400, 80, 100), "Woods", "forest"),
# Expanded map locations
Building(pygame.Rect(1800, 350, 240, 180), "Mall", "mall"),
Building(pygame.Rect(2100, 150, 300, 200), "Suburbs", "suburbs"),
Building(pygame.Rect(2400, 900, 260, 140), "Beach", "beach"),
Building(pygame.Rect(1900, 800, 220, 120), "Stall", "business"),
Building(pygame.Rect(2900, 500, 220, 160), "Tower", "boss"),
]
BUILDINGS = load_buildings()

FOREST_ENEMY_RECTS = [
pygame.Rect(300, 300, 60, 60),
Expand Down
66 changes: 66 additions & 0 deletions loaders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import json
import pygame
from typing import List, Dict

from entities import Building, Quest, SideQuest
from inventory import HOME_UPGRADES


def load_buildings(path: str = "data/buildings.json") -> List[Building]:
"""Load building definitions from a JSON file."""
with open(path) as f:
data = json.load(f)
buildings = [
Building(pygame.Rect(*b["rect"]), b["name"], b["type"]) for b in data
]
return buildings


# Quest check functions map by index

def _quest_check(idx: int, player) -> bool:
if idx == 0:
return player.money >= 200
if idx == 1:
return player.strength >= 5
if idx == 2:
return player.intelligence >= 5
if idx == 3:
return player.money >= 300
if idx == 4:
return player.strength >= 5
if idx == 5:
return player.enemies_defeated >= 3
if idx == 6:
return set(player.home_upgrades) == {u[0] for u in HOME_UPGRADES}
return False


def load_quests(path: str = "data/quests.json") -> List[Quest]:
"""Load quests from JSON, pairing with predefined checks."""
with open(path) as f:
data = json.load(f)
quests: List[Quest] = []
for i, q in enumerate(data):
desc = q.get("description", "")
next_idx = q.get("next_index")
quests.append(
Quest(desc, lambda p, _i=i: _quest_check(_i, p), next_index=next_idx)
)
return quests


def load_sidequests(path: str = "data/sidequests.json") -> Dict[str, SideQuest]:
"""Load side quest definitions and return mapping by name."""
with open(path) as f:
data = json.load(f)
side_list = []
for sq in data:
name = sq["name"]
desc = sq["description"]
target = sq["target"]
reward_amt = sq.get("reward", 0)
side_list.append(
SideQuest(name, desc, target, lambda p, amt=reward_amt: setattr(p, "money", p.money + amt))
)
return {q.name: q for q in side_list}
47 changes: 7 additions & 40 deletions quests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import random
from typing import List, Tuple

from loaders import load_quests, load_sidequests

import pygame

from entities import Player, Quest, Event, SideQuest, NPC
Expand Down Expand Up @@ -35,23 +37,7 @@
]

# Storyline quests completed in order
QUESTS: List[Quest] = [
Quest("Earn $200", lambda p: p.money >= 200),
Quest("Reach STR 5", lambda p: p.strength >= 5),
Quest("Reach INT 5", lambda p: p.intelligence >= 5),
Quest("Earn $300", lambda p: p.money >= 300, next_index=1),
Quest("Reach STR 5", lambda p: p.strength >= 5, next_index=2),
Quest(
"Defeat 3 alley thugs",
lambda p: p.enemies_defeated >= 3,
next_index=3,
),
Quest(
"Own every home upgrade",
lambda p: set(p.home_upgrades) == {u[0] for u in HOME_UPGRADES},
next_index=None,
),
]
QUESTS: List[Quest] = load_quests()

# Building targets for quest markers
QUEST_TARGETS = {
Expand All @@ -65,29 +51,10 @@
}

# Optional side quests
SIDE_QUEST = SideQuest(
"Bank Delivery",
"Deliver paperwork from the Bank to the Library",
"library",
lambda p: setattr(p, "money", p.money + 50),
)

NPC_QUEST = SideQuest(
"Courier Errand",
"Deliver a package from Sam to the Gym",
"gym",
lambda p: setattr(p, "money", p.money + 30),
)

MALL_QUEST = SideQuest(
"Beach Delivery",
"Pick up sunglasses from the Mall and bring them to the Beach",
"beach",
lambda p: setattr(p, "money", p.money + 40),
)

# Lookup active side quests by name
SIDE_QUESTS = {q.name: q for q in [SIDE_QUEST, NPC_QUEST, MALL_QUEST]}
SIDE_QUESTS = load_sidequests()
SIDE_QUEST = SIDE_QUESTS.get("Bank Delivery")
NPC_QUEST = SIDE_QUESTS.get("Courier Errand")
MALL_QUEST = SIDE_QUESTS.get("Beach Delivery")

# Friendly townsfolk found around the city
NPCS = [
Expand Down