Skip to content

Commit 7590561

Browse files
committed
Refactor weapon handling: move weapon stats to JSON file and implement dynamic loading; update weapon classes to inherit from FromData.
1 parent b50604f commit 7590561

3 files changed

Lines changed: 168 additions & 139 deletions

File tree

Discordia/GameLogic/Weapons.py

Lines changed: 53 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
"""
2-
Ostensibly, this file contains information about all weapons players can purchase and wield.
2+
Weapons players can purchase and wield.
33
4-
This module is just a mess. I've hardcoded everything, added a weird "FullyImplemented" class to signify non-abstract
5-
classes. It's written more like a C header file than an actual Python script. I don't even remember how half this stuff
6-
works. I was playing a lot of "Escape from Tarkov" at the time; the realism bug must have caught on.
4+
The classes here are behaviour -- how a weapon fires, falls off with range, or crits. The numbers that tell
5+
one gun from another live in data/weapons.json, keyed by class name, and reach the class through FromData.
76
"""
87

98
from __future__ import annotations
9+
10+
import json
1011
from abc import ABC
11-
from typing import Optional
12+
from typing import Any, Dict, Optional
1213

14+
from Discordia import DATA_FOLDER
1315
from Discordia.GameLogic import Actors, GameSpace
1416
from Discordia.GameLogic.Items import (
1517
Ammo,
@@ -19,6 +21,8 @@
1921
FullyImplemented,
2022
)
2123

24+
STATS_PATH = DATA_FOLDER / "weapons.json"
25+
2226

2327
class ProjectileType:
2428
Thrown = 0
@@ -44,6 +48,36 @@ class FiringAction:
4448
FullyAutomatic = 4
4549

4650

51+
_ENUM_FIELDS = {
52+
"caliber": Caliber,
53+
"action": FiringAction,
54+
"projectile_type": ProjectileType,
55+
}
56+
57+
58+
def load_stats(path=STATS_PATH) -> Dict[str, Dict[str, Any]]:
59+
"""Stat blocks by class name. Enum fields are written by name in the JSON, so the file stays readable."""
60+
return {
61+
weapon: {
62+
field: (
63+
getattr(_ENUM_FIELDS[field], value) if field in _ENUM_FIELDS else value
64+
)
65+
for field, value in stats.items()
66+
}
67+
for weapon, stats in json.loads(path.read_text(encoding="utf-8")).items()
68+
}
69+
70+
71+
STATS = load_stats()
72+
73+
74+
class FromData:
75+
"""Mixin for a weapon whose whole definition is its stat block. Must come first in the bases."""
76+
77+
def __init__(self):
78+
super().__init__(**STATS[type(self).__name__])
79+
80+
4781
class Weapon(Equipment, ABC):
4882
_base_damage: int
4983

@@ -210,166 +244,64 @@ class Pistol(Firearm, MainHandEquipment, ABC):
210244
pass
211245

212246

213-
class WeblyRevolver(Pistol, FullyImplemented):
247+
class WeblyRevolver(FromData, Pistol, FullyImplemented):
214248
"""
215249
Based on the Webly Mk. IV
216250
"""
217251

218-
name: str = "Webly Mk. IV Revolver"
219252

220-
def __init__(self):
221-
super().__init__(
222-
caliber=Caliber.IN_38,
223-
action=FiringAction.SemiAutomatic,
224-
capacity=6,
225-
range_falloff=0.5,
226-
base_damage=10,
227-
name=self.name,
228-
weightlb=2.4,
229-
)
230-
231-
232-
class M1911(Pistol, FullyImplemented):
253+
class M1911(FromData, Pistol, FullyImplemented):
233254
"""
234255
Based on the M1911
235256
"""
236257

237-
name: str = "M1911 Pistol"
238-
239-
def __init__(self):
240-
super().__init__(
241-
caliber=Caliber.IN_45,
242-
action=FiringAction.SemiAutomatic,
243-
capacity=7,
244-
range_falloff=0.4,
245-
base_damage=8,
246-
name=self.name,
247-
weightlb=2.44,
248-
)
249-
250258

251-
class APS(Pistol, SelectiveFire, FullyImplemented):
259+
class APS(FromData, Pistol, SelectiveFire, FullyImplemented):
252260
"""
253261
Based on the Stechkin automatic pistol (APS)
254262
"""
255263

256-
name: str = "Stechkin Automatic Pistol"
257-
258-
def __init__(self):
259-
super().__init__(
260-
caliber=Caliber.MM_9,
261-
action=FiringAction.SemiAutomatic,
262-
capacity=20,
263-
range_falloff=0.7,
264-
base_damage=4,
265-
name=self.name,
266-
weightlb=2.69,
267-
)
268-
269264

270265
class SMG(Firearm, MainHandEquipment, OffHandEquipment, ABC):
271266
pass
272267

273268

274-
class PPSh41(SMG, SelectiveFire, FullyImplemented):
269+
class PPSh41(FromData, SMG, SelectiveFire, FullyImplemented):
275270
"""
276271
Based on the PPSh-41 (Shpagin machine pistol)
277272
"""
278273

279-
name: str = "PPSh-41 (Shpagin machine pistol)"
280274

281-
def __init__(self):
282-
super().__init__(
283-
caliber=Caliber.MM_762,
284-
action=FiringAction.FullyAutomatic,
285-
capacity=35,
286-
range_falloff=0.55,
287-
base_damage=7,
288-
name=self.name,
289-
weightlb=8.0,
290-
)
291-
292-
293-
class OwenSMG(SMG, FullyImplemented):
275+
class OwenSMG(FromData, SMG, FullyImplemented):
294276
"""
295277
Based on the Owen Machine Carbine (Australian)
296278
"""
297279

298-
name: str = "Owen Machine Carbine"
299-
300-
def __init__(self):
301-
super().__init__(
302-
caliber=Caliber.MM_9,
303-
action=FiringAction.FullyAutomatic,
304-
capacity=33,
305-
range_falloff=0.7,
306-
base_damage=4,
307-
name=self.name,
308-
weightlb=9.33,
309-
)
310-
311280

312281
class Rifle(Firearm, MainHandEquipment, OffHandEquipment, ABC):
313282
pass
314283

315284

316-
class AK47(Rifle, SelectiveFire, FullyImplemented):
285+
class AK47(FromData, Rifle, SelectiveFire, FullyImplemented):
317286
"""
318287
Based on the AK-47
319288
"""
320289

321-
name: str = "AK-47"
322290

323-
def __init__(self):
324-
super().__init__(
325-
caliber=Caliber.MM_762,
326-
action=FiringAction.FullyAutomatic,
327-
capacity=30,
328-
range_falloff=0.35,
329-
base_damage=15,
330-
name=self.name,
331-
weightlb=7.7,
332-
)
333-
334-
335-
class HKG3(Rifle, SelectiveFire, FullyImplemented):
291+
class HKG3(FromData, Rifle, SelectiveFire, FullyImplemented):
336292
"""
337293
Based on the Heckler & Koch G3
338294
"""
339295

340-
name: str = "Heckler & Koch G3"
341-
342-
def __init__(self):
343-
super().__init__(
344-
caliber=Caliber.MM_762,
345-
action=FiringAction.FullyAutomatic,
346-
capacity=20,
347-
range_falloff=0.3,
348-
base_damage=14,
349-
name=self.name,
350-
weightlb=9.7,
351-
)
352-
353296

354-
class Jezail(Rifle, FullyImplemented):
297+
class Jezail(FromData, Rifle, FullyImplemented):
355298
"""
356299
Based on the Jezail Musket. Does 2x dmg if user is on a mountain.
357300
https://en.wikipedia.org/wiki/Jezail
358301
"""
359302

360-
name: str = "Jezail Musket"
361-
362303
def __init__(self):
363-
super().__init__(
364-
caliber=Caliber.BB,
365-
action=FiringAction.SingleShot,
366-
capacity=1,
367-
range_falloff=0.3,
368-
base_damage=20,
369-
name=self.name,
370-
weightlb=12,
371-
)
372-
304+
super().__init__()
373305
self.player: Optional[Actors.PlayerCharacter] = None
374306

375307
def on_equip(self, player_character: Actors.PlayerCharacter):
@@ -419,25 +351,11 @@ def mounted(self, new: bool):
419351
self._mounted = new
420352

421353

422-
class FNMinimi(MachineGun, FullyImplemented):
354+
class FNMinimi(FromData, MachineGun, FullyImplemented):
423355
"""
424356
Based on the FN Minimi
425357
"""
426358

427-
name: str = "FN Minimi"
428-
429-
def __init__(self):
430-
super().__init__(
431-
mountable=True,
432-
caliber=Caliber.MM_762,
433-
action=FiringAction.FullyAutomatic,
434-
capacity=100,
435-
range_falloff=0.25,
436-
base_damage=13,
437-
name=self.name,
438-
weightlb=15.1,
439-
)
440-
441359

442360
class Shotgun(Firearm, MainHandEquipment, OffHandEquipment, ABC):
443361
pellet_count: int
@@ -487,13 +405,9 @@ def __init__(self, cripple_chance: float, *args, **kwargs):
487405
self.cripple_chance = cripple_chance
488406

489407

490-
class Hammer(BluntWeapon, MainHandEquipment, FullyImplemented):
491-
492-
def __init__(self):
493-
super().__init__(cripple_chance=0.4, base_damage=10)
494-
408+
class Hammer(FromData, BluntWeapon, MainHandEquipment, FullyImplemented):
409+
pass
495410

496-
class Fist(BluntWeapon, MainHandEquipment, OffHandEquipment):
497411

498-
def __init__(self):
499-
super().__init__(base_value=0, cripple_chance=0.1, base_damage=2)
412+
class Fist(FromData, BluntWeapon, MainHandEquipment, OffHandEquipment):
413+
"""Everyone starts with these; not FullyImplemented, so no store stocks them."""

Discordia/data/weapons.json

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
{
2+
"WeblyRevolver": {
3+
"name": "Webly Mk. IV Revolver",
4+
"caliber": "IN_38",
5+
"action": "SemiAutomatic",
6+
"capacity": 6,
7+
"range_falloff": 0.5,
8+
"base_damage": 10,
9+
"weight_lb": 2.4
10+
},
11+
"M1911": {
12+
"name": "M1911 Pistol",
13+
"caliber": "IN_45",
14+
"action": "SemiAutomatic",
15+
"capacity": 7,
16+
"range_falloff": 0.4,
17+
"base_damage": 8,
18+
"weight_lb": 2.44
19+
},
20+
"APS": {
21+
"name": "Stechkin Automatic Pistol",
22+
"caliber": "MM_9",
23+
"action": "SemiAutomatic",
24+
"capacity": 20,
25+
"range_falloff": 0.7,
26+
"base_damage": 4,
27+
"weight_lb": 2.69
28+
},
29+
"PPSh41": {
30+
"name": "PPSh-41 (Shpagin machine pistol)",
31+
"caliber": "MM_762",
32+
"action": "FullyAutomatic",
33+
"capacity": 35,
34+
"range_falloff": 0.55,
35+
"base_damage": 7,
36+
"weight_lb": 8.0
37+
},
38+
"OwenSMG": {
39+
"name": "Owen Machine Carbine",
40+
"caliber": "MM_9",
41+
"action": "FullyAutomatic",
42+
"capacity": 33,
43+
"range_falloff": 0.7,
44+
"base_damage": 4,
45+
"weight_lb": 9.33
46+
},
47+
"AK47": {
48+
"name": "AK-47",
49+
"caliber": "MM_762",
50+
"action": "FullyAutomatic",
51+
"capacity": 30,
52+
"range_falloff": 0.35,
53+
"base_damage": 15,
54+
"weight_lb": 7.7
55+
},
56+
"HKG3": {
57+
"name": "Heckler & Koch G3",
58+
"caliber": "MM_762",
59+
"action": "FullyAutomatic",
60+
"capacity": 20,
61+
"range_falloff": 0.3,
62+
"base_damage": 14,
63+
"weight_lb": 9.7
64+
},
65+
"Jezail": {
66+
"name": "Jezail Musket",
67+
"caliber": "BB",
68+
"action": "SingleShot",
69+
"capacity": 1,
70+
"range_falloff": 0.3,
71+
"base_damage": 20,
72+
"weight_lb": 12
73+
},
74+
"FNMinimi": {
75+
"name": "FN Minimi",
76+
"mountable": true,
77+
"caliber": "MM_762",
78+
"action": "FullyAutomatic",
79+
"capacity": 100,
80+
"range_falloff": 0.25,
81+
"base_damage": 13,
82+
"weight_lb": 15.1
83+
},
84+
"Hammer": {
85+
"name": "Hammer",
86+
"cripple_chance": 0.4,
87+
"base_damage": 10
88+
},
89+
"Fist": {
90+
"name": "Fist",
91+
"base_value": 0,
92+
"cripple_chance": 0.1,
93+
"base_damage": 2
94+
}
95+
}

0 commit comments

Comments
 (0)