Files

196 lines
7.0 KiB
Python

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,))