You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

253 lines
9.3 KiB

  1. import random
  2. import string
  3. from django.db import models
  4. from django.db.models import Sum
  5. from django.utils import timezone
  6. def generate_code(length):
  7. return "".join([random.choice(string.ascii_lowercase)
  8. for i in range(length)])
  9. class Game(models.Model):
  10. ACCESS_CODE_LENGTH = 6
  11. access_code = models.CharField(db_index=True, unique=True,
  12. max_length=ACCESS_CODE_LENGTH)
  13. game_turn = models.IntegerField(default=0)
  14. GAME_PHASE_LOBBY = 0
  15. GAME_PHASE_GROWTH = 1
  16. GAME_PHASE_SPIRIT = 2
  17. GAME_PHASE_GROWTH_SPIRIT = 3
  18. GAME_PHASE_FAST = 4
  19. GAME_PHASE_BLIGHTED_ISLAND = 5
  20. GAME_PHASE_FEAR = 6
  21. GAME_PHASE_FEAR_CARDS = [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]
  22. GAME_PHASE_ENGLAND_BUILD = 100
  23. GAME_PHASE_RAVAGE = 101
  24. GAME_PHASE_BUILD = 102
  25. GAME_PHASE_EXPLORE = 103
  26. GAME_PHASE_SLOW = 104
  27. GAME_PHASE_END = 120
  28. game_phase = models.IntegerField(default=GAME_PHASE_LOBBY)
  29. created = models.DateTimeField()
  30. ended = models.DateTimeField(null=True, default=None)
  31. combined_growth_spirit = models.BooleanField(default=False)
  32. england_build = models.BooleanField(default=False)
  33. fear_per_card = models.IntegerField()
  34. # from http://stackoverflow.com/a/11821832
  35. def save(self, *args, **kwargs):
  36. # object is being created, thus no primary key field yet
  37. if not self.pk:
  38. # Make sure access_code is unique before using it.
  39. access_code = generate_code(Game.ACCESS_CODE_LENGTH)
  40. while Game.objects.filter(access_code=access_code).exists():
  41. access_code = generate_code(Game.ACCESS_CODE_LENGTH)
  42. self.access_code = access_code
  43. self.created = timezone.now()
  44. if self.ended is None and self.game_phase == Game.GAME_PHASE_END:
  45. self.ended = timezone.now()
  46. super(Game, self).save(*args, **kwargs)
  47. def next_turn_and_phase(self):
  48. if self.game_phase in [Game.GAME_PHASE_LOBBY, Game.GAME_PHASE_SLOW]:
  49. turn = self.game_turn+1
  50. if self.combined_growth_spirit:
  51. return (turn, Game.GAME_PHASE_GROWTH_SPIRIT)
  52. else:
  53. return (turn, Game.GAME_PHASE_GROWTH)
  54. def next_phase():
  55. if self.game_phase == Game.GAME_PHASE_SPIRIT:
  56. return Game.GAME_PHASE_FAST
  57. elif self.game_phase == Game.GAME_PHASE_BLIGHTED_ISLAND:
  58. if self.num_available_fear_cards() == 0:
  59. return Game.GAME_PHASE_FEAR
  60. else:
  61. return Game.GAME_PHASE_FEAR_CARDS[0]
  62. elif self.game_phase == Game.GAME_PHASE_FEAR or\
  63. self.game_phase in Game.GAME_PHASE_FEAR_CARDS:
  64. num_fear = self.fear_cards_in_current_fear_phase()
  65. fear_so_far = self.game_phase - Game.GAME_PHASE_FEAR
  66. if num_fear < fear_so_far:
  67. return self.game_phase + 1
  68. else:
  69. if self.england_build:
  70. return Game.GAME_PHASE_ENGLAND_BUILD
  71. else:
  72. return Game.GAME_PHASE_RAVAGE
  73. else:
  74. return self.game_phase + 1
  75. return (self.game_turn, next_phase())
  76. def get_current_phase(self):
  77. return self.phase_set.filter(game_turn=self.game_turn,
  78. game_phase=self.game_phase).first()
  79. def get_current_total_fear(self):
  80. current_phase = self.get_current_phase()
  81. if current_phase:
  82. return current_phase.starting_fear +\
  83. current_phase.fear_this_phase()
  84. else:
  85. return 0
  86. def get_fear_to_next_card(self):
  87. total_fear = self.get_current_total_fear()
  88. leftover_fear = total_fear // self.fear_per_card
  89. return self.fear_per_card - leftover_fear
  90. def _fear_phase(self, previous):
  91. if self.game_phase >= Game.GAME_PHASE_FEAR and not previous:
  92. turn = self.game_turn
  93. else:
  94. turn = self.game_turn - 1
  95. return self.phase_set.filter(
  96. game_turn=turn,
  97. game_phase__in=[Game.GAME_PHASE_FEAR,
  98. Game.GAME_PHASE_FEAR_CARDS[0]]).first()
  99. def _fear_cards_as_of_fear_phase(self, previous):
  100. fear_phase = self._fear_phase(previous=previous)
  101. if fear_phase:
  102. return fear_phase.initial_fear // self.fear_per_card
  103. else:
  104. return 0
  105. def fear_cards_in_current_fear_phase(self):
  106. previous_fear_cards = self._fear_cards_as_of_fear_phase(previous=True)
  107. current_fear_cards = self._fear_cards_as_of_fear_phase(previous=False)
  108. return current_fear_cards - previous_fear_cards
  109. def num_available_fear_cards(self):
  110. recent_fear_fear_cards = self._fear_cards_as_of_fear_phase(
  111. previous=False)
  112. current_fear = self.get_current_total_fear()
  113. current_fear_cards = current_fear // self.fear_per_card
  114. return current_fear_cards - recent_fear_fear_cards
  115. def advance_phase(self):
  116. current_phase = self.get_current_phase()
  117. current_time = timezone.now()
  118. if current_phase:
  119. current_phase.ended = current_time
  120. current_phase.save()
  121. initial_fear = current_phase.starting_fear +\
  122. current_phase.fear_this_phase()
  123. else:
  124. initial_fear = 0
  125. (next_turn, next_phase) = self.next_turn_and_phase()
  126. self.game_turn = next_turn
  127. self.game_phase = next_phase
  128. self.save()
  129. next_phase = self.get_current_phase()
  130. if next_phase is None:
  131. Phase.objects.create(
  132. game=self,
  133. game_turn=next_turn,
  134. game_phase=next_phase,
  135. initial_fear=initial_fear,
  136. started=current_time)
  137. else:
  138. next_phase.started = current_time
  139. next_phase.initial_fear = initial_fear
  140. next_phase.ended = None
  141. next_phase.save()
  142. @staticmethod
  143. def get_name_for_phase_id(phase):
  144. if phase == Game.GAME_PHASE_LOBBY:
  145. return "Game Setup"
  146. elif phase == Game.GAME_PHASE_GROWTH:
  147. return "Growth"
  148. elif phase == Game.GAME_PHASE_SPIRIT:
  149. return "Spirit"
  150. elif phase == Game.GAME_PHASE_GROWTH_SPIRIT:
  151. return "Growth/Spirit"
  152. elif phase == Game.GAME_PHASE_FAST:
  153. return "Fast Actions"
  154. elif phase == Game.GAME_PHASE_BLIGHTED_ISLAND:
  155. return "Blighted Island"
  156. elif phase == Game.GAME_PHASE_FEAR:
  157. return "Fear (no fear cards)"
  158. elif phase in Game.GAME_PHASE_FEAR_CARDS:
  159. return "Fear Card #" + str(phase-Game.GAME_PHASE_FEAR)
  160. elif phase == Game.GAME_PHASE_ENGLAND_BUILD:
  161. return "England Extra Build"
  162. elif phase == Game.GAME_PHASE_RAVAGE:
  163. return "Ravage"
  164. elif phase == Game.GAME_PHASE_BUILD:
  165. return "Build"
  166. elif phase == Game.GAME_PHASE_EXPLORE:
  167. return "Explore"
  168. elif phase == Game.GAME_PHASE_SLOW:
  169. return "Slow Actions"
  170. elif phase == Game.GAME_PHASE_END:
  171. return "Game Over"
  172. def get_current_phase_name(self):
  173. res = Game.get_name_for_phase_id(self.game_phase)
  174. if self.game_phase in Game.GAME_PHASE_FEAR_CARDS:
  175. res += ' (of %d)' % self.fear_cards_in_current_fear_phase()
  176. return res
  177. class Player(models.Model):
  178. game = models.ForeignKey(Game, on_delete=models.CASCADE, db_index=True)
  179. name = models.CharField(max_length=80)
  180. SPIRIT_NAMES = [
  181. "Lightning's Swift Strike",
  182. "River Surges in Sunlight",
  183. "Vital Strength of the Earth",
  184. "Shadows Flicker Like Flame",
  185. "Thunderspeaker",
  186. "A Spread of Rampant Green",
  187. "Ocean's Hungry Grasp",
  188. "Bringer of Dreams and Nightmares",
  189. "Keeper of the Forbidden Wilds",
  190. "Sharp Fangs Behind the Leaves",
  191. "Serpent Slumbering Beneath the Island",
  192. "Heart of the Wildfire",
  193. ]
  194. spirit = models.IntegerField()
  195. order = models.IntegerField()
  196. ready = models.BooleanField(default=False)
  197. unique_together = (("game", "name"), ("game", "order"))
  198. def get_spirit_name(self):
  199. return Player.SPIRIT_NAMES[self.spirit]
  200. class Phase(models.Model):
  201. game = models.ForeignKey(Game, on_delete=models.CASCADE, db_index=True)
  202. game_turn = models.IntegerField()
  203. game_phase = models.IntegerField()
  204. started = models.DateTimeField()
  205. ended = models.DateTimeField(null=True, default=None)
  206. # fear total at the start of this phase
  207. starting_fear = models.IntegerField(default=0)
  208. unique_together = (("game", "game_turn", "game_phase"))
  209. def fear_this_phase(self):
  210. return self.fear_set.aggregate(
  211. fear=Sum('pure_fear')
  212. + Sum('towns_destroyed')
  213. + 2*Sum('cities_destroyed'))['fear'] or 0
  214. class Fear(models.Model):
  215. phase = models.ForeignKey(Phase, on_delete=models.CASCADE, db_index=True)
  216. player = models.ForeignKey(Player, on_delete=models.CASCADE, db_index=True)
  217. effect = models.IntegerField(default=0)
  218. # "pure" = not from destroying a town or city
  219. pure_fear = models.IntegerField()
  220. towns_destroyed = models.IntegerField()
  221. cities_destroyed = models.IntegerField()