Add: armor plates support with new database tables and loadout functionality

fix: only required mods are applied to guns for weight management.
This commit is contained in:
serversdwn
2026-02-24 16:08:58 +00:00
parent 84768ae587
commit 9d572f5d15
7 changed files with 471 additions and 126 deletions

71
app.py
View File

@@ -357,7 +357,7 @@ def loadout():
tab = request.args.get("tab", "guns")
sort = request.args.get("sort", "weight_asc")
guns = armor = helmets = headwear = backpacks = rigs = []
guns = armor = helmets = headwear = backpacks = rigs = plates = []
builder_guns = builder_armor = builder_helmets = builder_rigs = builder_backpacks = []
requires = request.args.getlist("requires") # list of slot_nameids that must exist
min_class = request.args.get("min_class", 0, type=int)
@@ -443,6 +443,14 @@ def loadout():
ORDER BY {sort_frag}
""", (min_capacity, min_capacity, min_class, min_class)).fetchall()
elif tab == "plates":
plates = conn.execute(f"""
SELECT * FROM gear_items
WHERE category = 'plate'
AND (? = 0 OR armor_class >= ?)
ORDER BY {sort_frag}
""", (min_class, min_class)).fetchall()
elif tab == "builder":
builder_guns = conn.execute("SELECT id, name, weight_kg FROM gear_items WHERE category='gun' ORDER BY name").fetchall()
builder_armor = conn.execute("SELECT id, name, weight_kg FROM gear_items WHERE category='armor' ORDER BY name").fetchall()
@@ -450,12 +458,16 @@ def loadout():
builder_rigs = conn.execute("SELECT id, name, weight_kg FROM gear_items WHERE category='rig' ORDER BY name").fetchall()
builder_backpacks = conn.execute("SELECT id, name, weight_kg FROM gear_items WHERE category='backpack' ORDER BY name").fetchall()
# IDs of carriers that have at least one open plate slot (shell weight only)
open_slot_rows = conn.execute("SELECT DISTINCT carrier_id FROM armor_open_slots").fetchall()
carrier_ids_with_open_slots = {row["carrier_id"] for row in open_slot_rows}
conn.close()
return render_template(
"loadout.html",
tab=tab, sort=sort,
guns=guns, armor=armor, helmets=helmets, headwear=headwear,
backpacks=backpacks, rigs=rigs,
backpacks=backpacks, rigs=rigs, plates=plates,
slot_filters=LOADOUT_SLOT_FILTERS,
requires=requires,
min_class=min_class, min_capacity=min_capacity,
@@ -464,6 +476,7 @@ def loadout():
builder_helmets=builder_helmets,
builder_rigs=builder_rigs,
builder_backpacks=builder_backpacks,
carrier_ids_with_open_slots=carrier_ids_with_open_slots,
)
@@ -506,17 +519,19 @@ def gun_detail(gun_id):
if row["mod_id"]:
slots[sid]["mods"].append(dict(row))
# Key slots to show at top (highlighted)
# Split into required vs optional slots
KEY_SLOTS = {"mod_muzzle", "mod_magazine"}
ordered_slots = [slots[s] for s in slot_order]
key_slots = [s for s in ordered_slots if s["slot_nameid"] in KEY_SLOTS]
other_slots = [s for s in ordered_slots if s["slot_nameid"] not in KEY_SLOTS]
# Required slots (always needed) shown at top — key slots (magazine/muzzle) highlighted
key_slots = [s for s in ordered_slots if s["required"] and s["slot_nameid"] in KEY_SLOTS]
req_slots = [s for s in ordered_slots if s["required"] and s["slot_nameid"] not in KEY_SLOTS]
optional_slots = [s for s in ordered_slots if not s["required"]]
# Lightest total (base + lightest per slot)
# Lightest total (base + lightest per REQUIRED slot only)
lightest_total = (gun["weight_kg"] or 0) + sum(
s["mods"][0]["weight_kg"]
for s in ordered_slots
if s["mods"] and s["mods"][0]["weight_kg"] is not None
if s["required"] and s["mods"] and s["mods"][0]["weight_kg"] is not None
)
conn.close()
@@ -524,7 +539,8 @@ def gun_detail(gun_id):
"gun_detail.html",
gun=gun,
key_slots=key_slots,
other_slots=other_slots,
req_slots=req_slots,
optional_slots=optional_slots,
lightest_total=lightest_total,
)
@@ -571,6 +587,45 @@ def gun_slots_json(gun_id):
return jsonify(result)
@app.route("/loadout/carrier/<carrier_id>/slots.json")
def carrier_slots_json(carrier_id):
"""Returns open plate slots and allowed plates for a carrier (armor or rig)."""
conn = get_db()
rows = conn.execute("""
SELECT aos.slot_nameid, aos.zones,
p.id AS plate_id, p.name AS plate_name, p.short_name AS plate_short,
p.weight_kg, p.armor_class, p.durability, p.material
FROM armor_open_slots aos
LEFT JOIN armor_slot_plates asp ON asp.carrier_id = aos.carrier_id
AND asp.slot_nameid = aos.slot_nameid
LEFT JOIN gear_items p ON p.id = asp.plate_id
WHERE aos.carrier_id = ?
ORDER BY aos.slot_nameid, p.armor_class DESC, p.weight_kg ASC
""", (carrier_id,)).fetchall()
conn.close()
# Group by slot
slots = {}
slot_order = []
for row in rows:
sn = row["slot_nameid"]
if sn not in slots:
slots[sn] = {"slot_nameid": sn, "zones": row["zones"], "plates": []}
slot_order.append(sn)
if row["plate_id"]:
slots[sn]["plates"].append({
"id": row["plate_id"],
"name": row["plate_name"],
"short_name": row["plate_short"],
"weight_kg": row["weight_kg"],
"armor_class": row["armor_class"],
"durability": row["durability"],
"material": row["material"],
})
return jsonify([slots[s] for s in slot_order])
@app.route("/loadout/save-build", methods=["POST"])
def save_build():
data = request.get_json() or {}