import math
from typing import Callable, Dict
from twisted.internet import reactor
from pyspades.constants import (RIFLE_WEAPON, SMG_WEAPON, SHOTGUN_WEAPON,
HEAD, TORSO, ARMS, LEGS, CLIP_TOLERANCE)
[docs]class BaseWeapon:
shoot = False
reloading = False
id = None # type: int
shoot_time = None
next_shot = 0
start = None
# Weapon parameters
# Total number of rounds
stock = None # type: int
# Number of rounds that fit in the weapon at once
ammo = None # type: int
# If the weapon should be reloaded one round at a time like the shotgun
slow_reload = False
# Time between shots
delay = 0.0
# Time between reloading and being able to shoot again
reload_time = 0.0
# Dict of damages
damage = None # type: Dict[int, int]
def __init__(self, reload_callback: Callable) -> None:
self.reload_callback = reload_callback
self.reset()
[docs] def restock(self) -> None:
self.current_stock = self.stock
[docs] def reset(self) -> None:
self.shoot = False
if self.reloading:
self.reload_call.cancel()
self.reloading = False
self.current_ammo = self.ammo
self.current_stock = self.stock
[docs] def set_shoot(self, value: bool) -> None:
if value == self.shoot:
return
current_time = reactor.seconds()
if value:
self.start = current_time
if self.current_ammo <= 0:
return
elif self.reloading and not self.slow_reload:
return
self.shoot_time = max(current_time, self.next_shot)
if self.reloading:
self.reloading = False
self.reload_call.cancel()
else:
ammo = self.current_ammo
self.current_ammo = self.get_ammo(True)
self.next_shot = self.shoot_time + self.delay * (
ammo - self.current_ammo)
self.shoot = value
[docs] def reload(self) -> None:
if self.reloading:
return
ammo = self.get_ammo()
if not self.current_stock or ammo >= self.ammo:
return
elif self.slow_reload and self.shoot and ammo:
return
self.reloading = True
self.set_shoot(False)
self.current_ammo = ammo
self.reload_call = reactor.callLater(self.reload_time, self.on_reload)
[docs] def on_reload(self) -> None:
self.reloading = False
if self.slow_reload:
self.current_ammo += 1
self.current_stock -= 1
self.reload_callback()
self.reload()
else:
new_stock = max(0, self.current_stock - (
self.ammo - self.current_ammo))
self.current_ammo += self.current_stock - new_stock
self.current_stock = new_stock
self.reload_callback()
[docs] def get_ammo(self, no_max: bool = False) -> int:
if self.shoot:
dt = reactor.seconds() - self.shoot_time
ammo = self.current_ammo - max(0, int(
math.ceil(dt / self.delay)))
else:
ammo = self.current_ammo
if no_max:
return ammo
return max(0, ammo)
[docs] def is_empty(self, tolerance=CLIP_TOLERANCE) -> bool:
return self.get_ammo(True) < -tolerance or not self.shoot
[docs] def get_damage(self, value, position1, position2) -> int:
return self.damage[value]
[docs]class Rifle(BaseWeapon):
id = RIFLE_WEAPON
name = 'Rifle'
delay = 0.5
ammo = 10
stock = 50
reload_time = 2.5
slow_reload = False
damage = {
TORSO: 49,
HEAD: 100,
ARMS: 33,
LEGS: 33
}
[docs]class SMG(BaseWeapon):
id = SMG_WEAPON
name = 'SMG'
delay = 0.11 # actually 0.1, but due to AoS scheduling, it's usually 0.11
ammo = 30
stock = 120
reload_time = 2.5
slow_reload = False
damage = {
TORSO: 29,
HEAD: 75,
ARMS: 18,
LEGS: 18
}
[docs]class Shotgun(BaseWeapon):
id = SHOTGUN_WEAPON
name = 'Shotgun'
delay = 1.0
ammo = 6
stock = 48
reload_time = 0.5
slow_reload = True
damage = {
TORSO: 27,
HEAD: 37,
ARMS: 16,
LEGS: 16
}
WEAPONS = {
RIFLE_WEAPON: Rifle,
SMG_WEAPON: SMG,
SHOTGUN_WEAPON: Shotgun,
}