196 lines
7.0 KiB
Python
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,)) |