import time import math import jwt import bcrypt from .database import get_connection from .game_data import BUILDING_DEFINITIONS, GRID_SIZES, STORAGE_CAPACITIES SECRET_KEY = "grimhold_dark_fantasy_2026_super_secret_jwt" JWT_ALGORITHM = "HS256" def hash_password(password: str) -> str: return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8") def verify_password(plain: str, hashed: str) -> bool: return bcrypt.checkpw(plain.encode("utf-8"), hashed.encode("utf-8")) def create_access_token(user_id: int, username: str) -> str: payload = { "sub": str(user_id), "username": username, "exp": time.time() + 86400 * 14 # 14 days } return jwt.encode(payload, SECRET_KEY, algorithm=JWT_ALGORITHM) def decode_access_token(token: str) -> dict: try: return jwt.decode(token, SECRET_KEY, algorithms=[JWT_ALGORITHM]) except Exception: return None def calculate_costs(b_type: str, level: int): defn = BUILDING_DEFINITIONS.get(b_type) if not defn: return None, 10 mult = defn["cost_multiplier"] ** (level - 1) t_mult = defn["time_multiplier"] ** (level - 1) costs = { "wood": math.floor(defn["base_cost"]["wood"] * mult), "iron": math.floor(defn["base_cost"]["iron"] * mult), "food": math.floor(defn["base_cost"]["food"] * mult), "essence": math.floor(defn["base_cost"]["essence"] * mult), } duration = math.floor(defn["base_time"] * t_mult) return costs, duration def update_player_state(conn, user_id: int): now = time.time() # 1. Fetch user & buildings & queues cur = conn.cursor() cur.execute("SELECT * FROM users WHERE id = ?", (user_id,)) user = cur.fetchone() if not user: return None cur.execute("SELECT * FROM resources WHERE user_id = ?", (user_id,)) res = cur.fetchone() # 2. Check and finish building queue items cur.execute("SELECT * FROM building_queue WHERE user_id = ? AND finishes_at <= ? ORDER BY finishes_at ASC", (user_id, now)) finished_items = cur.fetchall() fortress_lvl = user["fortress_level"] grid_w = user["grid_w"] grid_h = user["grid_h"] max_storage = res["max_storage"] for item in finished_items: b_type = item["b_type"] target_lvl = item["target_level"] defn = BUILDING_DEFINITIONS.get(b_type, {}) new_hp = defn.get("base_hp", 100) + (target_lvl - 1) * defn.get("hp_per_lvl", 50) if item["building_id"]: # Upgrade existing cur.execute(""" UPDATE buildings SET level = ?, max_hp = ?, hp = ? WHERE id = ? AND user_id = ? """, (target_lvl, new_hp, new_hp, item["building_id"], user_id)) else: # New placement cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, ?, ?, ?, ?, ?, ?, 'active') """, (user_id, b_type, target_lvl, item["x"], item["y"], new_hp, new_hp)) # Core building upgrade expands the whole sanctuary! if b_type == "core": fortress_lvl = target_lvl new_size = GRID_SIZES.get(fortress_lvl, {"w": 16 + (fortress_lvl-1)*4, "h": 10 + (fortress_lvl-1)*2}) grid_w = new_size["w"] grid_h = new_size["h"] max_storage = STORAGE_CAPACITIES.get(fortress_lvl, 2000 * fortress_lvl) cur.execute(""" UPDATE users SET fortress_level = ?, grid_w = ?, grid_h = ? WHERE id = ? """, (fortress_lvl, grid_w, grid_h, user_id)) cur.execute("UPDATE resources SET max_storage = ? WHERE user_id = ?", (max_storage, user_id)) cur.execute("DELETE FROM building_queue WHERE id = ?", (item["id"],)) # 3. Calculate production rates from all active economy buildings cur.execute("SELECT * FROM buildings WHERE user_id = ? AND status = 'active'", (user_id,)) all_b = cur.fetchall() wood_rate = 5.0 / 60.0 # base 5/min passive iron_rate = 3.0 / 60.0 # base 3/min passive food_rate = 5.0 / 60.0 # base 5/min passive essence_rate = 0.5 / 60.0 # base 0.5/min passive for b in all_b: b_type = b["b_type"] lvl = b["level"] defn = BUILDING_DEFINITIONS.get(b_type) if not defn or "base_rate" not in defn: continue rate = defn["base_rate"] + (lvl - 1) * defn.get("rate_per_lvl", 0) if b_type == "woodcutter": wood_rate += rate elif b_type == "mine": iron_rate += rate elif b_type == "farm": food_rate += rate # 4. Calculate resource delta dt = max(0.0, now - user["last_tick_at"]) new_wood = min(max_storage, res["wood"] + dt * wood_rate) new_iron = min(max_storage, res["iron"] + dt * iron_rate) new_food = min(max_storage, res["food"] + dt * food_rate) new_essence = min(max_storage, res["essence"] + dt * essence_rate) cur.execute(""" UPDATE resources SET wood = ?, iron = ?, food = ?, essence = ? WHERE user_id = ? """, (new_wood, new_iron, new_food, new_essence, user_id)) cur.execute("UPDATE users SET last_tick_at = ? WHERE id = ?", (now, user_id)) return { "rates": { "wood_min": round(wood_rate * 60, 1), "iron_min": round(iron_rate * 60, 1), "food_min": round(food_rate * 60, 1), "essence_min": round(essence_rate * 60, 1), } } def create_starter_fortress(conn, user_id: int): # Places core treasury at x=12, y=4 (for 16x10 start) # A few starter walls and basic economy now = time.time() cur = conn.cursor() # 1 Core Treasury cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, 'core', 1, 12, 4, 800, 800, 'active') """, (user_id,)) # 1 Woodcutter cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, 'woodcutter', 1, 12, 1, 250, 250, 'active') """, (user_id,)) # 1 Mine cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, 'mine', 1, 12, 7, 300, 300, 'active') """, (user_id,)) # 1 Ballista Tower cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, 'ballista', 1, 9, 4, 260, 260, 'active') """, (user_id,)) # Starter defensive walls funneling path starter_walls = [ (8, 2), (8, 3), (8, 5), (8, 6), (10, 2), (10, 6) ] for wx, wy in starter_walls: cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, 'wall', 1, ?, ?, 350, 350, 'active') """, (user_id, wx, wy)) # 1 Hidden Spike Trap in the funnel entrance cur.execute(""" INSERT INTO buildings (user_id, b_type, level, x, y, hp, max_hp, status) VALUES (?, 'spike_trap', 1, 8, 4, 50, 50, 'active') """, (user_id,))