diff --git a/app.py b/app.py index 518b854..3ea413c 100644 --- a/app.py +++ b/app.py @@ -15,7 +15,7 @@ DB_FILE = Path("tycoon.db") # --- Database Management --- def init_db(): - """Initializes the database and creates tables if they don't exist.""" + """Initializes the database and creates/alters tables if they don't exist.""" try: conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() @@ -23,9 +23,17 @@ def init_db(): CREATE TABLE IF NOT EXISTS players ( nickname TEXT PRIMARY KEY, money INTEGER NOT NULL, - population INTEGER NOT NULL + population INTEGER NOT NULL, + happiness REAL NOT NULL DEFAULT 0.5 ) """) + # Add happiness column if it doesn't exist for migrations + try: + cursor.execute("ALTER TABLE players ADD COLUMN happiness REAL NOT NULL DEFAULT 0.5") + logger.info("Added 'happiness' column to players table.") + except sqlite3.OperationalError: + pass # Column already exists + cursor.execute(""" CREATE TABLE IF NOT EXISTS buildings ( key TEXT PRIMARY KEY, @@ -111,10 +119,12 @@ game_state: Dict[str, Any] = { building_data = { 'residential': { 'cost': 100, 'population': 10 }, 'commercial': { 'cost': 250, 'income': 5 }, - 'industrial': { 'cost': 500, 'income': 20 }, - 'park': { 'cost': 80, 'population_bonus': 5 }, + 'industrial': { 'cost': 500, 'income': 20, 'happiness_impact': -0.02 }, + 'park': { 'cost': 80, 'population_bonus': 5, 'happiness_impact': 0.01 }, 'powerplant': { 'cost': 1000, 'income': 50 }, - 'road': { 'cost': 20 } + 'road': { 'cost': 20 }, + 'police': { 'cost': 600, 'happiness_bonus': 0.1 }, + 'stadium': {'cost': 5000, 'income': 150, 'happiness_impact': 0.05 } } # --- Game Logic --- @@ -140,33 +150,52 @@ async def game_loop(): player = game_state["players"][nickname] income = 0 population = 0 - + base_happiness = 0.5 # Start with a neutral base happiness + player_buildings = {k: v for k, v in game_state["buildings"].items() if v["owner"] == nickname} - # Calculate base income and population + # Calculate base income, population, and happiness impacts for building in player_buildings.values(): b_type = building["type"] - if b_type == 'residential': - population += building_data['residential']['population'] - elif b_type == 'commercial': - income += building_data['commercial']['income'] - elif b_type == 'industrial': - income += building_data['industrial']['income'] - elif b_type == 'powerplant': - income += building_data['powerplant']['income'] + b_data = building_data.get(b_type, {}) - # Calculate park adjacency bonuses + if b_type == 'residential': + population += b_data.get('population', 0) + elif 'income' in b_data: + if b_type == 'commercial': + # Commercial income is modified by happiness + happiness_multiplier = max(0.1, player.get('happiness', 0.5)) + income += b_data['income'] * (1 + happiness_multiplier) + else: + income += b_data.get('income', 0) + + if 'happiness_impact' in b_data: + base_happiness += b_data['happiness_impact'] + + # Calculate adjacency bonuses parks = {k: v for k, v in player_buildings.items() if v["type"] == 'park'} + police_stations = {k: v for k, v in player_buildings.items() if v["type"] == 'police'} + for park_key in parks: for neighbor_key in get_neighbors(park_key): neighbor = player_buildings.get(neighbor_key) if neighbor and neighbor["type"] == 'residential': population += building_data['park']['population_bonus'] + for police_key in police_stations: + for neighbor_key in get_neighbors(police_key): + neighbor = player_buildings.get(neighbor_key) + if neighbor and neighbor["type"] == 'residential': + base_happiness += building_data['police']['happiness_bonus'] / 4 # Distribute bonus over 4 neighbors + + # Finalize and clamp values + final_happiness = max(0, min(1, base_happiness)) + player["money"] += income player["population"] = population + player["happiness"] = final_happiness - db_execute("UPDATE players SET money = ?, population = ? WHERE nickname = ?", (player["money"], player["population"], nickname)) + db_execute("UPDATE players SET money = ?, population = ?, happiness = ? WHERE nickname = ?", (player["money"], player["population"], player["happiness"], nickname)) if game_state["players"]: await manager.broadcast(json.dumps({ @@ -187,14 +216,15 @@ async def on_startup(): async def websocket_endpoint(websocket: WebSocket, nickname: str): await manager.connect(websocket, nickname) - player_data = db_fetchone("SELECT money, population FROM players WHERE nickname = ?", (nickname,)) + player_data = db_fetchone("SELECT money, population, happiness FROM players WHERE nickname = ?", (nickname,)) if player_data: - game_state["players"][nickname] = { "money": player_data["money"], "population": player_data["population"] } + game_state["players"][nickname] = { "money": player_data["money"], "population": player_data["population"], "happiness": player_data["happiness"] } else: initial_money = 1500 initial_pop = 0 - db_execute("INSERT INTO players (nickname, money, population) VALUES (?, ?, ?)", (nickname, initial_money, initial_pop)) - game_state["players"][nickname] = {"money": initial_money, "population": initial_pop} + initial_happiness = 0.5 + db_execute("INSERT INTO players (nickname, money, population, happiness) VALUES (?, ?, ?, ?)", (nickname, initial_money, initial_pop, initial_happiness)) + game_state["players"][nickname] = {"money": initial_money, "population": initial_pop, "happiness": initial_happiness} await websocket.send_text(json.dumps({ "type": "full_state", "buildings": game_state["buildings"] })) await manager.broadcast(json.dumps({ "type": "status_update", "message": f"'{nickname}' has joined the game!" })) @@ -248,4 +278,3 @@ async def websocket_endpoint(websocket: WebSocket, nickname: str): async def root(): return FileResponse("index.html") - diff --git a/index.html b/index.html index 1c5f3af..91b02fc 100644 --- a/index.html +++ b/index.html @@ -1,39 +1,39 @@ - + Tiny Tycoon 3D (Multiplayer)