/landarmy/defcon/armygroup.py

https://github.com/joskid/bellum
Python | 156 lines | 123 code | 22 blank | 11 comment | 26 complexity | 72393beb1af1760dfc0ec586d0123720 MD5 | raw file
  1. from bellum.meta import MGID
  2. from bellum.landarmy.defcon.objects import INFANTRY, ARMOR, SUPPORT, MULTIATTACK
  3. from bellum.landarmy.defcon.specials import Tarpit
  4. from copy import deepcopy
  5. from random import uniform
  6. class ArmyGroup(object):
  7. '''An army belonging to a single player'''
  8. def isTargetable(self, utype=None):
  9. '''army group is not targetable when only living units are support.
  10. when type is specified, will return whether units of given type are targetable'''
  11. if utype == None:
  12. return (self.aou[INFANTRY] != 0) or (self.aou[ARMOR] != 0)
  13. else:
  14. return self.aou[utype] > 0
  15. def nextTurn(self): pass
  16. def countAllUnits(self): return self.aou[INFANTRY] + self.aou[ARMOR] + self.aou[SUPPORT]
  17. def countUnits(self, utype):
  18. '''if utype == None then counts all units that may be targeted'''
  19. if utype == MULTIATTACK:
  20. return self.aou[INFANTRY] + self.aou[ARMOR]
  21. else:
  22. return self.aou[utype]
  23. def _blast(self, utype, uid):
  24. '''just removes an unit from table. nothing else. Uid is unique type, not array identifier'''
  25. uindice = -1
  26. for uuid, unit, cnt in self.units[utype]:
  27. uindice += 1
  28. if uuid==uid:
  29. break
  30. if uindice == -1: raise Exception, 'Did not found!'
  31. self.units[utype][uindice][2] -= 1 # decrement unit
  32. if self.units[utype][uindice][2] == 0: # if all units of given type were killed
  33. del self.units[utype][uindice] # remove them
  34. def __choice(self, utype):
  35. '''picks a random unit from given unittype with chances proportional to its amount.
  36. returns it's self.units[utype] seq indice
  37. unittype MUST BE INFANTRY or ARMOR'''
  38. total_prob = self.aou[utype]
  39. chosen = uniform(0, total_prob)
  40. cumulative = 0
  41. indice = 0
  42. for id, unit, amount in self.units[utype]:
  43. cumulative += amount
  44. if cumulative > chosen:
  45. return indice
  46. indice += 1
  47. def receiveFire(self, shot, army, canUseDebris=True, canUseMarkerlights=True):
  48. '''@army parent army object. Receive a shot. Perform needed modifications. Returns a cool tuple'''
  49. assert shot.type in (ARMOR, INFANTRY), 'Invalid shot type '+str(shot.type)
  50. uindice = self.__choice(shot.type)
  51. unit = self.units[shot.type][uindice][1]
  52. wasHit = unit.receiveFire(shot, army, canUseDebris=canUseDebris, canUseMarkerlights=canUseMarkerlights)
  53. if wasHit:
  54. self.units[shot.type][uindice][2] -= 1 # decrement unit
  55. if self.units[shot.type][uindice][2] == 0: # if all units of given type were killed
  56. del self.units[shot.type][uindice] # remove them
  57. if shot.type == ARMOR: # we killed an armoured unit
  58. army.maxdebris += 1
  59. army.debris += 1
  60. self.aou[shot.type] -= 1 # decrement units of given type
  61. if unit.special(Tarpit) != False: # SPECIAL RULE: TARPIT
  62. return (True, (Tarpit, ))
  63. return (wasHit, ())
  64. def targetUnit(self, shotObject, specialRules=None):
  65. '''Target one of my units with shotObject and optional specialRules seq of objects.
  66. Returns a tuple (unit type, unit id, unit object, amount of units).'''
  67. # now we should target a unit
  68. total_prob = self.aou[shotObject.type]
  69. chosen = uniform(0, total_prob)
  70. cumulative = 0
  71. for id, definition, amount in self.units[shotObject.type]:
  72. cumulative += amount
  73. if cumulative > chosen:
  74. return (shotObject.type, id, definition, amount)
  75. def writeback(self):
  76. '''write surviving units to rootGarrisonObject. Does not save'''
  77. for i in xrange(0, MGID+1):
  78. self.rootGarrisonObject[i] = 0
  79. for uclass in self.units:
  80. for u in uclass:
  81. if u[0] == None:
  82. continue
  83. self.rootGarrisonObject[u[0]] = u[2]
  84. def __init__(self, rootGarrisonObject, owner, is_offense):
  85. '''this just generates an army group. It does not apply bonuses.
  86. true if this party acts in offense'''
  87. from bellum.common.fixtures.landarmy_stats import UNIT_STATS
  88. agh = UNIT_STATS[owner.race][{True:0, False:1}[is_offense]]
  89. self.owner = owner
  90. self.rootGarrisonObject = rootGarrisonObject
  91. self.units = [[], [], []]
  92. self.aou = [0, 0, 0]
  93. for i in xrange(0, MGID+1):
  94. if rootGarrisonObject[i] > 0:
  95. # we need it copied along with all of its weapon, so we use deepcopy
  96. # append a list(unit id, Unit object, amount of units)
  97. self.units[agh[i].type].append([i, deepcopy(agh[i]), rootGarrisonObject[i]])
  98. self.aou[agh[i].type] += rootGarrisonObject[i]
  99. # sort by initiative
  100. for cat in (INFANTRY, ARMOR, SUPPORT): self.units[cat].sort(key=lambda x: x[1].initiative, reverse=True)
  101. def bonus_im(self, bonus):
  102. for id, iunit, cnt in self.units[INFANTRY]:
  103. iunit.bonus_im(bonus)
  104. def bonus_to_ctinfantry(self, bonus):
  105. for id, iunit, cnt in self.units[INFANTRY]:
  106. iunit.bonus_ct(bonus)
  107. def bonus_to_ctarmor(self, bonus):
  108. for id, aunit, cnt in self.units[ARMOR]:
  109. aunit.bonus_ct(bonus)
  110. def bonus_to_ct(self, bonus):
  111. self.bonus_to_ctinfantry(bonus)
  112. self.bonus_to_ctarmor(bonus)
  113. def bonus_to_st(self, bonus, support=False):
  114. for id, iunit, cnt in self.units[INFANTRY]:
  115. iunit.bonus_st(bonus)
  116. for id, aunit, cnt in self.units[ARMOR]:
  117. aunit.bonus_st(bonus)
  118. if support: self.bonus_to_st_support(bonus)
  119. def bonus_to_st_support(self, bonus):
  120. for id, sunit, cnt in self.units[SUPPORT]:
  121. sunit.bonus_st(bonus)
  122. def bonus_to_aos_support(self, bonus):
  123. for id, sunit, cnt in self.units[SUPPORT]:
  124. sunit.bonus_aos(bonus)
  125. def additive_to_mt(self, add):
  126. '''adds to ALL UNITS!!!'''
  127. for units in self.units:
  128. for id, sunit, cnt in units:
  129. sunit.additive_to_mt(add)