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
98from __future__ import annotations
9+
10+ import json
1011from abc import ABC
11- from typing import Optional
12+ from typing import Any , Dict , Optional
1213
14+ from Discordia import DATA_FOLDER
1315from Discordia .GameLogic import Actors , GameSpace
1416from Discordia .GameLogic .Items import (
1517 Ammo ,
1921 FullyImplemented ,
2022)
2123
24+ STATS_PATH = DATA_FOLDER / "weapons.json"
25+
2226
2327class 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+
4781class 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
270265class 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
312281class 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
442360class 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."""
0 commit comments