From 3f2d79a038b96f72c6e86f5f340a49d687d05328 Mon Sep 17 00:00:00 2001 From: RT Date: Fri, 24 Jul 2026 10:18:06 -0500 Subject: [PATCH] BTFrstrm: add weapon location spreadsheets (Special1/2 and rear-facing) --- BTFrstrm/gen_weapon_spreadsheets.py | 111 ++++++++++++++++++++++++++ BTFrstrm/rear_facing_weapons.csv | 64 +++++++++++++++ BTFrstrm/special_weapon_locations.csv | 45 +++++++++++ 3 files changed, 220 insertions(+) create mode 100644 BTFrstrm/gen_weapon_spreadsheets.py create mode 100644 BTFrstrm/rear_facing_weapons.csv create mode 100644 BTFrstrm/special_weapon_locations.csv diff --git a/BTFrstrm/gen_weapon_spreadsheets.py b/BTFrstrm/gen_weapon_spreadsheets.py new file mode 100644 index 00000000..6fd1988b --- /dev/null +++ b/BTFrstrm/gen_weapon_spreadsheets.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +"""Generate special_weapon_locations.csv and rear_facing_weapons.csv from stock mech subsystems.""" +import csv +from pathlib import Path + +BASE = Path("Gameleap/mw4/Content") +MECH_TABLE = BASE / "Tables" / "MechTable.tbl" +OUT_DIR = Path("BTFrstrm") + +def parse_ini(path): + sections, cur_sec, cur_dict = [], None, {} + with open(path, 'r', encoding='latin-1', errors='replace') as f: + for raw in f: + line = raw.strip() + if line.startswith('[') and line.endswith(']'): + if cur_sec is not None: + sections.append((cur_sec, cur_dict)) + cur_sec, cur_dict = line[1:-1], {} + elif '=' in line and cur_sec is not None: + k, _, v = line.partition('=') + cur_dict[k.strip()] = v.strip() + if cur_sec is not None: + sections.append((cur_sec, cur_dict)) + return sections + +def find_dir_ci(parent, name): + """Case-insensitive directory lookup for Linux.""" + exact = parent / name + if exact.exists(): + return exact + name_lower = name.lower() + for child in parent.iterdir(): + if child.is_dir() and child.name.lower() == name_lower: + return child + return None + +def weapon_model(model_path): + return Path(model_path.replace('\\', '/')).stem + +# Read chassis list from MechTable.tbl +chassis_list = [] +with open(MECH_TABLE, 'r', encoding='latin-1') as f: + for raw in f: + line = raw.strip() + if not line or line.startswith('//') or line.startswith('[') or '=' not in line: + continue + name, _, rel = line.partition('=') + # rel is like "Mechs\Atlas\Atlas.instance" + parts = rel.strip().replace('\\', '/').split('/') + chassis_list.append((name.strip(), parts)) # parts = ['Mechs', 'Atlas', 'Atlas.instance'] + +print(f"Found {len(chassis_list)} chassis in MechTable.tbl") + +HDR = ['Chassis', 'InternalLocation', 'Model', 'Site', 'AmmoCount', 'GroupIndex', 'WeaponFacing'] +special_rows, rear_rows, missing = [], [], [] + +mechs_dir = BASE / "Mechs" + +for chassis_name, path_parts in chassis_list: + # path_parts[1] is the mech directory name (e.g. 'Atlas', 'MadCat_MkII', 'Urbanmech') + if len(path_parts) < 2: + missing.append(chassis_name) + continue + + mech_dir = find_dir_ci(mechs_dir, path_parts[1]) + if not mech_dir: + missing.append(f"{chassis_name} (dir not found: {path_parts[1]})") + continue + + subs = [f for f in mech_dir.glob('*.subsystems') if f.suffix == '.subsystems'] + if not subs: + missing.append(f"{chassis_name} (no .subsystems in {mech_dir.name})") + continue + + subsys_file = subs[0] + sections = parse_ini(subsys_file) + + for sec_name, fields in sections: + if 'WeaponSubsystem' not in fields.get('Model', ''): + continue + + loc = fields.get('InternalLocation', '') + model = weapon_model(fields.get('Model', '')) + site = fields.get('Site', '') + ammo = fields.get('AmmoCount', '') + group = fields.get('GroupIndex', '') + facing = fields.get('WeaponFacing', '') + + if loc in ('Special1', 'Special2'): + special_rows.append([chassis_name, loc, model, site, ammo, group, facing]) + + if facing and facing != '0': + rear_rows.append([chassis_name, loc, model, site, ammo, group, facing]) + +# Write CSVs +out1 = OUT_DIR / 'special_weapon_locations.csv' +with open(out1, 'w', newline='') as f: + w = csv.writer(f) + w.writerow(HDR) + w.writerows(special_rows) + +out2 = OUT_DIR / 'rear_facing_weapons.csv' +with open(out2, 'w', newline='') as f: + w = csv.writer(f) + w.writerow(HDR) + w.writerows(rear_rows) + +print(f"Spreadsheet 1 ? Special1/2 weapon locations: {len(special_rows)} rows -> {out1}") +print(f"Spreadsheet 2 ? Non-forward WeaponFacing: {len(rear_rows)} rows -> {out2}") +if missing: + print(f"\nSkipped: {missing}") diff --git a/BTFrstrm/rear_facing_weapons.csv b/BTFrstrm/rear_facing_weapons.csv new file mode 100644 index 00000000..d85d6800 --- /dev/null +++ b/BTFrstrm/rear_facing_weapons.csv @@ -0,0 +1,64 @@ +Chassis,InternalLocation,Model,Site,AmmoCount,GroupIndex,WeaponFacing +Archer,LeftTorso,LRM15,site_ldmissile,24,2,2 +Archer,RightTorso,LRM15,site_rdmissile,24,2,2 +Ares,Special2,ClanLRM10,site_lmissileport,24,2,2 +Ares,Special2,ClanLRM10,site_missileport,24,2,2 +Ares,Special2,ClanLRM10,site_rmissileport,24,2,2 +AssassinII,Special1,LRM5,site_missileport,24,2,2 +Atlas,LeftTorso,LRM20,site_lmissileport,12,2,2 +Avatar,LeftTorso,LRM5,site_lmissileport,24,2,2 +Avatar,RightTorso,LRM5,site_rmissileport,24,2,2 +Avatar,LeftTorso,LRM10,site_lmissileport,24,2,2 +Avatar,RightTorso,LRM10,site_rmissileport,24,2,2 +Awesome,CenterTorso,LRM5,site_ctorsoport,24,2,2 +Battlemaster,RightTorso,MediumLaser,site_rdtorsoport,,1,1 +Battlemaster,LeftTorso,MediumLaser,site_ldtorsoport,,1,1 +BattlemasterIIC,RightTorso,ClanERMediumLaser,site_rdtorsoport,,1,1 +BattlemasterIIC,LeftTorso,ClanERMediumLaser,site_ldtorsoport,,1,1 +Black Lanner,Special2,ClanSRM6,site_rmissileport,15,2,2 +Black Lanner,Special1,ClanLRM10,site_lmissileport,24,2,2 +Bushwacker,Special1,LRM5,site_missileport,24,2,2 +Bushwacker,Special1,LRM5,site_missileport,24,2,2 +Catapult,LeftArm,LRM20,site_lmissileport,12,2,2 +Catapult,RightArm,LRM20,site_rmissileport,12,2,2 +Cauldron-Born,Special1,ClanLRM10,site_rmissileport,24,2,2 +Cauldron-Born,Special2,ClanLRM10,site_lmissileport,24,2,2 +Chimera,RightTorso,LRM5,site_missileport,24,2,2 +Chimera,RightTorso,LRM5,site_missileport,24,2,2 +Chimera,RightTorso,LRM5,site_missileport,24,2,2 +Chimera,RightTorso,LRM5,site_missileport,24,2,2 +Cyclops,LeftTorso,LRM10,site_lmissile,24,2,2 +Cougar,LeftTorso,ClanLRM10,site_lmissileport,12,2,2 +Cougar,RightTorso,ClanLRM10,site_rmissileport,12,2,2 +Deimos,Special1,ClanLRM15,site_lmissileport,16,2,2 +Deimos,Special2,ClanLRM15,site_rmissileport,16,2,2 +Dragon,CenterTorso,LRM10,site_missleport,24,2,2 +Flea,LeftTorso,SmallLaser,site_ltorsoport,,1,1 +Flea,RightTorso,SmallLaser,site_rtorsoport,,1,1 +Grizzly,LeftTorso,CLANLRM10,site_missleport,24,2,2 +Hellhound,LeftTorso,ClanLRM10,site_missileport,12,2,2 +Mad Cat,Special1,ClanLRM10,site_lmissileport,24,2,2 +Mad Cat,Special2,ClanLRM10,site_rmissileport,24,2,2 +Mad Cat MKII,Special2,ClanLRM15,site_lmissileport,16,2,2 +Mad Cat MKII,Special1,ClanLRM15,site_rmissileport,16,2,2 +Masakari,LeftTorso,ClanLRM10,site_lmissleport,24,2,2 +Masakari,LeftTorso,ClanLRM10,site_ltorsoport,24,2,2 +Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2 +Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2 +Mauler,LeftTorso,LRM5,site_lmissileport,24,2,2 +Mauler,RightTorso,LRM5,site_rmissileport,24,2,2 +Mauler,RightTorso,LRM5,site_rmissileport,24,2,2 +Mauler,RightTorso,LRM5,site_rmissileport,24,2,2 +Raven,RightTorso,LRM5,site_rtorsoport,24,2,2 +Sunder,LeftTorso,MediumLaser,site_ldtorsoport,,1,1 +Sunder,RightTorso,MediumLaser,site_rdtorsoport,,1,1 +Templar,LeftTorso,MediumPulseLaser,site_ldrtorsoport,,1,1 +Templar,RightTorso,MediumPulseLaser,site_rdrtorsoport,,1,1 +Thor,Special1,CLANLRM10,site_missileport,24,2,2 +Uziel,Special1,LRM10,site_missileport,24,2,2 +Vulture,LeftTorso,ClanLRM5,site_lmissileport,24,2,2 +Vulture,RightTorso,ClanLRM5,site_rmissileport,24,2,2 +Vulture,LeftTorso,ClanLRM10,site_lmissileport,24,2,2 +Vulture,RightTorso,ClanLRM10,site_rmissileport,24,2,2 +Wolfhound,CenterTorso,ClanERMediumLaser,site_reartorsoport,,1,1 +Zeus,RightTorso,MediumPulseLaser,site_reartorsoport,,1,1 diff --git a/BTFrstrm/special_weapon_locations.csv b/BTFrstrm/special_weapon_locations.csv new file mode 100644 index 00000000..6f7ab535 --- /dev/null +++ b/BTFrstrm/special_weapon_locations.csv @@ -0,0 +1,45 @@ +Chassis,InternalLocation,Model,Site,AmmoCount,GroupIndex,WeaponFacing +Ares,Special1,ClanERSmallLaser,site_lgunport,,1, +Ares,Special1,ClanERSmallLaser,site_rgunport,,1, +Ares,Special2,ClanLRM10,site_lmissileport,24,2,2 +Ares,Special2,ClanLRM10,site_missileport,24,2,2 +Ares,Special2,ClanLRM10,site_rmissileport,24,2,2 +AssassinII,Special1,LRM5,site_missileport,24,2,2 +Atlas,Special1,AC20,site_rtorsoport,10,1, +Atlas,Special2,SRM6,site_llmissile,15,2, +Avatar,Special1,MachineGun,site_ltorsoport,200,1, +Avatar,Special1,MachineGun,site_rtorsoport,200,1, +Battlemaster,Special2,PPC,site_rdgunport,,1, +Battlemaster,Special1,SRM6,site_missile,15,2, +BattlemasterIIC,Special1,ClanSSRM6,site_missile,15,2, +Behemoth,Special1,LargeLaser,site_ctorsoport,,1, +BehemothII,Special1,LargeLaser,site_ctorsoport,,1, +Black Lanner,Special2,ClanSRM6,site_rmissileport,15,2,2 +Black Lanner,Special1,ClanLRM10,site_lmissileport,24,2,2 +Brigand,Special2,MediumLaser,site_ltorsoport,,1, +Brigand,Special1,MediumLaser,site_rtorsoport,,1, +Bushwacker,Special1,LRM5,site_missileport,24,2,2 +Bushwacker,Special1,LRM5,site_missileport,24,2,2 +Cauldron-Born,Special1,ClanLRM10,site_rmissileport,24,2,2 +Cauldron-Born,Special2,ClanLRM10,site_lmissileport,24,2,2 +Cyclops,Special1,MediumPulseLaser,site_gunport,,1, +Deimos,Special1,ClanLRM15,site_lmissileport,16,2,2 +Deimos,Special2,ClanLRM15,site_rmissileport,16,2,2 +Hauptmann,Special1,ClanUltraAC20,site_rutorsoport,16,1, +Hellspawn,Special1,SSRM2,site_rmissileport,50,2, +HollanderII,Special2,SmallPulseLaser,site_cltorsoport,,1, +HollanderII,Special2,SmallPulseLaser,site_crtorsoport,,1, +HollanderII,Special1,GaussRifle,site_rtorsoport,16,1, +Hunchback,Special1,AC10,site_rutorsoport,20,1, +Loki,Special1,ClanSSRM6,site_missileport,15,2, +Mad Cat,Special1,ClanLRM10,site_lmissileport,24,2,2 +Mad Cat,Special2,ClanLRM10,site_rmissileport,24,2,2 +Mad Cat MKII,Special2,ClanLRM15,site_lmissileport,16,2,2 +Mad Cat MKII,Special1,ClanLRM15,site_rmissileport,16,2,2 +Rifleman,Special1,MediumLaser,site_ltorsoport,,1, +Rifleman,Special1,MediumLaser,site_rtorsoport,,1, +Solitaire,Special1,ClanERLargeLaser,site_rtorsoport,,1, +Thor,Special1,CLANLRM10,site_missileport,24,2,2 +Uziel,Special1,LRM10,site_missileport,24,2,2 +Vulture,Special1,ClanMachineGun,site_ltorsoport,200,1, +Vulture,Special1,ClanMachineGun,site_rtorsoport,200,1,