group.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. from random import randint
  2. from typing import Type
  3. import pygame
  4. from jogai.character import Character
  5. from jogai.translations import gettext as _
  6. class Group(pygame.sprite.Group):
  7. """
  8. Generic group of sprites.
  9. Examples:
  10. >>> g = Group()
  11. """
  12. def __init__(self, *args, **kwgargs):
  13. super().__init__(*args, **kwgargs)
  14. class RandomGroup(Group):
  15. """
  16. Group of randomly distributed characters.
  17. Examples:
  18. >>> protagonist = Character('protagonist.png')
  19. >>> rg = RandomGroup(protagonist, 5, (100, 500), (0, 300))
  20. """
  21. def __init__(
  22. self,
  23. model: Type['Character'],
  24. amount: int,
  25. xrange: tuple,
  26. yrange: tuple,
  27. ):
  28. # TODO: check model type
  29. if not isinstance(amount, int):
  30. raise TypeError(
  31. _('The amount of characters must be a integer greater than 1.')
  32. )
  33. if amount < 2:
  34. raise ValueError(
  35. _('The amount of characters must be a integer greater than 1.')
  36. )
  37. if isinstance(xrange, list):
  38. xrange = tuple(xrange)
  39. if isinstance(yrange, list):
  40. yrange = tuple(yrange)
  41. if not isinstance(xrange, tuple) or not isinstance(yrange, tuple):
  42. raise TypeError(_('Both x and y ranges must be tuples.'))
  43. if len(xrange) != 2 or len(yrange) != 2:
  44. raise ValueError(
  45. _('Both x and y ranges must have only two items (min, max)')
  46. )
  47. if xrange[0] > xrange[1] or yrange[0] > yrange[1]:
  48. raise ValueError(
  49. _('Both x and y ranges must be sorted (min, max)')
  50. )
  51. self.amount = amount
  52. self.xrange = xrange
  53. self.yrange = yrange
  54. characters_list = []
  55. for pos in range(self.amount):
  56. new_character = model.copy()
  57. new_character.set_position(
  58. (
  59. randint(*xrange),
  60. randint(*yrange),
  61. )
  62. )
  63. characters_list.append(new_character)
  64. super().__init__(characters_list)
  65. class RectangleGroup(Group):
  66. """
  67. Group of characters forming a rectangle.
  68. Examples:
  69. >>> protagonist = Character('protagonist.png')
  70. >>> rg = RectangleGroup(protagonist, 5, 3)
  71. """
  72. def __init__(self, model: Type['Character'], base: int, height: int):
  73. # TODO: check model type
  74. if not isinstance(base, int) or not isinstance(height, int):
  75. raise TypeError(_('Both the base and rectangle must be integers.'))
  76. if base < 1 or height < 1:
  77. raise ValueError(
  78. _('Both the base and rectangle must be positive.')
  79. )
  80. if base == 1 and height == 1:
  81. raise ValueError(
  82. _(
  83. 'At least one of the dimensions of the rectangle must be greater than 1.'
  84. )
  85. )
  86. self.base = base
  87. self.height = height
  88. self.amount = base * height
  89. characters_list = []
  90. for row in range(self.height):
  91. for col in range(self.base):
  92. if not characters_list:
  93. new_character = model
  94. else:
  95. new_character = model.copy()
  96. new_character.set_position(
  97. (
  98. model.x + model.width * col,
  99. model.y + model.height * row,
  100. )
  101. )
  102. characters_list.append(new_character)
  103. super().__init__(characters_list)
  104. class SquareGroup(RectangleGroup):
  105. """
  106. Group of characters forming a square.
  107. Examples:
  108. >>> protagonist = Character('protagonist.png')
  109. >>> sg = SquareGroup(protagonist, 3)
  110. """
  111. def __init__(self, model: Type['Character'], side: int):
  112. # TODO: check model type
  113. if not isinstance(side, int):
  114. raise TypeError(_('The side of the square must be an integer.'))
  115. if side < 2:
  116. raise ValueError(
  117. _('The side of the square must be greater than 1.')
  118. )
  119. self.side = side
  120. super().__init__(model, side, side)
  121. class StairsGroup(Group):
  122. """
  123. Group of characters forming stairs.
  124. Examples:
  125. >>> protagonist = Character('protagonist.png')
  126. >>> sg = StairsGroup(protagonist, 3)
  127. """
  128. def __init__(
  129. self,
  130. model: Type['Character'],
  131. steps: int,
  132. horizontal_flip: bool = False,
  133. vertical_flip: bool = False,
  134. ):
  135. # TODO: check model type and adjust the position of elements
  136. if not isinstance(steps, int):
  137. raise TypeError(_('The steps of the stairs must be an integer.'))
  138. if steps < 2:
  139. raise ValueError(
  140. _('The steps of the stairs must be greater than 1.')
  141. )
  142. if not isinstance(horizontal_flip, bool) or not isinstance(
  143. vertical_flip, bool
  144. ):
  145. raise TypeError(
  146. _('Both horizontal and vertical flips must be a boolean.')
  147. )
  148. self.steps = steps
  149. self.amount = steps * (steps + 1) // 2
  150. self.horizontal_flip = horizontal_flip
  151. self.vertical_flip = vertical_flip
  152. horizontal_sign = 1 if horizontal_flip else -1
  153. vertical_sign = -1 if vertical_flip else 1
  154. characters_list = [model]
  155. for row in range(2, self.steps + 1):
  156. for col in range(row):
  157. new_character = model.copy()
  158. new_character.name = model.name
  159. new_character.set_position(
  160. (
  161. model.x + model.width * col * horizontal_sign,
  162. model.y + model.height * (row - 1) * vertical_sign,
  163. )
  164. )
  165. characters_list.append(new_character)
  166. super().__init__(characters_list)