From 24d5711d82e1a70c82eb878daaa5ed330adf71fe Mon Sep 17 00:00:00 2001 From: Gabor Guzmics Date: Wed, 24 May 2017 21:10:35 +0200 Subject: [PATCH] forgot to add da cookie. --- src/scon/stuff/__init__.py | 0 src/scon/stuff/ship_battle.py | 208 ++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 src/scon/stuff/__init__.py create mode 100644 src/scon/stuff/ship_battle.py diff --git a/src/scon/stuff/__init__.py b/src/scon/stuff/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/scon/stuff/ship_battle.py b/src/scon/stuff/ship_battle.py new file mode 100644 index 0000000..b2d10c6 --- /dev/null +++ b/src/scon/stuff/ship_battle.py @@ -0,0 +1,208 @@ +import random + +class ShipModule(object): + """ A generic ship module equippable on a ship """ + name = 'Module' # class based variable. all ShipModule's share this one string. + def __init__(self): + self.ship = None # instance based variable. every instance has its own ship. + + def update(self): + """ This function is called by the ship every gamestep """ + pass + +class DefensiveLayer(ShipModule): + """ some kind of defensive layer. a ship will use its defensive layers against incoming damage """ + name = 'Armor' + + def __init__(self, maximal, + resistances=None, + current=None): + super(DefensiveLayer, self).__init__() # === ShipModule.__init__(self) + self.maximal = maximal + self.resistances = resistances or {} + self.current = current or maximal + + def reduce_damage(self, damage, damage_type=None): + # reduces incoming damage by absorbing it somehow. + if self.current > 0: + # only resist if i have still current. + if damage_type: + # if there was a damage type, we look up in our resistances: + res = self.resistances.get(damage_type, 0) + if res: + damage -= damage * res + # we call get_damage to absorb damage, and it returns the rest of the damage. + return self.get_damage(damage) + else: + # we have no life, damage passes through. + return damage + + def get_damage(self, damage): + # the module itself gets an amount of damage. + self.current -= damage + if self.current <= 0: + rest_damage = 0 - self.current + self.current = 0 + return rest_damage + return 0 + +class Shields(DefensiveLayer): + """ Shields can recharge after a delay. """ + name = 'Shields' + RECHARGE_DELAY = 5 + + def __init__(self, maximal, + resistances=None, current=None, recharge=0): + super(Shields, self).__init__(maximal, resistances, current) + self.recharge = recharge + # we save this little integer, which if bigger than zero, indicates there was a hit not long ago. + self._last_hit = 0 + + def get_damage(self, damage): + # we override get_damage to remember we got hit. + self._last_hit = self.RECHARGE_DELAY + return super(Shields, self).get_damage(damage) + + def update(self): + # we override update function to implement a recharge, and slowly reset the _last_hit. + if self._last_hit > 0: + self._last_hit -= 1 + else: + if self.current < self.maximal: + self.current += self.recharge + self.current = min(self.current, self.maximal) + +class Weapon(ShipModule): + """ Weapons can fire + On this class we save some class variables, which act as default values. + - we can subclass different weapons overriding those values for all weapons of that kind. + - we can still make individual weapons with different stats. + """ + name = 'Weapon' + DEFAULT_DAMAGE = 10 + DEFAULT_COOLDOWN = 10 + DEFAULT_DAMAGE_TYPE = None + + def __init__(self, ammo, + damage=None, + cooldown=None, + damage_type=None): + super(Weapon, self).__init__() + self.damage = damage or self.DEFAULT_DAMAGE + self.cooldown = cooldown or self.DEFAULT_COOLDOWN + self.damage_type = damage_type or self.DEFAULT_DAMAGE_TYPE + self._heat = 0 + self.ammo = ammo + + + def fire(self, other_ship, me=None): + if not other_ship: + return + if self._heat == 0: + other_ship.on_hit(self.damage, self.damage_type, me or self.ship) + self._heat = self.cooldown + + def update(self): + if self._heat > 0: + self._heat -= 1 + +########################################################################## +class Spaceship(object): + + def __init__(self, name, modules=None): + self.name = name + self.modules = modules or [] + self.alive = True + # we define an additional automatically called function + self.equip_default() + + def equip_default(self): + # you can use this to equip stuff by default in a subclass, without the need to override __init__. + pass + + # we define some methods, that get called by the game itself: + def on_spawn(self): + print(f"{self.name} appears in space.") + + def on_hit(self, damage, damage_type, source): + for mod in self.modules: + if isinstance(mod, DefensiveLayer): + damage = mod.reduce_damage(damage, damage_type) + if damage == 0: + return + if damage > 0: + # i am dead. + self.alive = False + + def health_status(self): + for mod in self.modules: + if isinstance(mod, DefensiveLayer): + if mod.current < mod.maximal: + print (f'{mod.name} has sustained {mod.maximal-mod.current} damage') + + def fire(self, target): + for mod in self.modules: + if isinstance(mod, Weapon): + mod.fire(target, self) + + def update(self): + for mod in self.modules: + if isinstance(mod, ShipModule): + mod.update() + +class FreeForAll(object): + def __init__(self): + self.ships = [] + self.running = False + + def add_ship(self, ship): + self.ships.append(ship) + + def run(self): + for ship in self.ships: + ship.on_spawn() + all_dead = False + while not all_dead: + all_dead = True + for ship in self.ships: + if ship.alive: + all_dead = False + targets = list(set(self.ships)) + targets.remove(ship) + if len(targets) > 0: + ship.fire(random.choice(targets)) + else: + print (f'{ship.name} has won!') + return + if ship.alive: + ship.update() + self.describe() + + def describe(self): + if len(self.ships) == 0: + print("There are no ships left.") + self.running = False + return + for ship in self.ships: + if not ship.alive: + print(f'The ship {ship.name} is destroyed.') + self.ships.remove(ship) + continue + print(f'Current ship {ship.name} status report:') + ship.health_status() + + +def main(): + game = FreeForAll() + game.add_ship(Spaceship('Rocinante', modules=[DefensiveLayer(350), Weapon(100), Weapon(100)])) + game.add_ship(Spaceship('Protoss Carrier', modules=[Shields(1000), DefensiveLayer(100), Weapon(15, 2), Weapon(15, 2)])) + + # and add 100 raptors. + for x in range(1, 100): + game.add_ship(Spaceship(f'Raptor {x}', modules=[Shields(100), DefensiveLayer(50), Weapon(10,5), Weapon(10,5)])) + + game.run() + +if __name__ == '__main__': + main() + \ No newline at end of file