data/states/battle.py (view raw)
1"""This is the state that handles battles against
2monsters"""
3
4import random
5import pygame as pg
6from .. import tools, battlegui, observer
7from .. components import person, attack, attackitems
8from .. import constants as c
9
10
11class Battle(tools._State):
12 def __init__(self):
13 super(Battle, self).__init__()
14 self.game_data = {}
15 self.current_time = 0.0
16 self.timer = 0.0
17 self.allow_input = False
18 self.allow_info_box_change = False
19 self.name = 'battle'
20 self.state = None
21
22 self.player = None
23 self.attack_animations = None
24 self.sword = None
25 self.enemy_index = 0
26 self.attacked_enemy = None
27 self.attacking_enemy = None
28 self.enemy_group = None
29 self.enemy_pos_list = []
30 self.enemy_list = []
31
32 self.background = None
33 self.info_box = None
34 self.arrow = None
35 self.select_box = None
36 self.player_health_box = None
37 self.select_action_state_dict = {}
38 self.next = None
39 self.observers = []
40 self.damage_points = pg.sprite.Group()
41
42 def startup(self, current_time, game_data):
43 """Initialize state attributes"""
44 self.current_time = current_time
45 self.timer = current_time
46 self.game_data = game_data
47 self.state = c.SELECT_ACTION
48 self.next = game_data['last state']
49
50 self.player = self.make_player()
51 self.attack_animations = pg.sprite.Group()
52 self.sword = attackitems.Sword(self.player)
53 self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
54 self.background = self.make_background()
55 self.info_box = battlegui.InfoBox(game_data)
56 self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
57 self.info_box)
58 self.select_box = battlegui.SelectBox()
59 self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
60 self.game_data)
61
62 self.select_action_state_dict = self.make_selection_state_dict()
63 self.observers = [observer.Battle(self)]
64 self.player.observers.extend(self.observers)
65
66 @staticmethod
67 def make_background():
68 """Make the blue/black background"""
69 background = pg.sprite.Sprite()
70 surface = pg.Surface(c.SCREEN_SIZE).convert()
71 surface.fill(c.BLACK_BLUE)
72 background.image = surface
73 background.rect = background.image.get_rect()
74 background_group = pg.sprite.Group(background)
75
76 return background_group
77
78 @staticmethod
79 def make_enemies():
80 """Make the enemies for the battle. Return sprite group"""
81 pos_list = []
82
83 for column in range(3):
84 for row in range(3):
85 x = (column * 100) + 100
86 y = (row * 100) + 100
87 pos_list.append([x, y])
88
89 enemy_group = pg.sprite.Group()
90
91 for enemy in range(random.randint(1, 6)):
92 enemy_group.add(person.Person('devil', 0, 0,
93 'down', 'battle resting'))
94
95 for i, enemy in enumerate(enemy_group):
96 enemy.rect.topleft = pos_list[i]
97 enemy.image = pg.transform.scale2x(enemy.image)
98 enemy.index = i
99 enemy.level = 1
100 enemy.health = enemy.level * 7
101
102 enemy_list = [enemy for enemy in enemy_group]
103
104 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
105
106 @staticmethod
107 def make_player():
108 """Make the sprite for the player's character"""
109 player = person.Player('left', 630, 220, 'battle resting', 1)
110 player.image = pg.transform.scale2x(player.image)
111
112 return player
113
114 def make_selection_state_dict(self):
115 """
116 Make a dictionary of states with arrow coordinates as keys.
117 """
118 pos_list = self.arrow.make_select_action_pos_list()
119 state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
120 return dict(zip(pos_list, state_list))
121
122 def update(self, surface, keys, current_time):
123 """Update the battle state"""
124 self.current_time = current_time
125 self.check_input(keys)
126 self.check_timed_events()
127 self.check_if_battle_won()
128 self.enemy_group.update(current_time)
129 self.player.update(keys, current_time)
130 self.attack_animations.update()
131 self.info_box.update()
132 self.arrow.update(keys)
133 self.sword.update(current_time)
134 self.damage_points.update()
135
136 self.draw_battle(surface)
137
138 def check_input(self, keys):
139 """
140 Check user input to navigate GUI.
141 """
142 if self.allow_input:
143 if keys[pg.K_RETURN]:
144 self.notify(c.END_BATTLE)
145
146 elif keys[pg.K_SPACE]:
147 if self.state == c.SELECT_ACTION:
148 self.state = self.select_action_state_dict[
149 self.arrow.rect.topleft]
150 self.notify(self.state)
151
152 elif self.state == c.SELECT_ENEMY:
153 self.state = c.PLAYER_ATTACK
154 self.notify(self.state)
155
156 elif self.state == c.SELECT_ITEM or self.state == c.SELECT_MAGIC:
157 if self.arrow.index == (len(self.arrow.pos_list) - 1):
158 self.state = c.SELECT_ACTION
159 self.notify(self.state)
160
161 self.allow_input = False
162
163 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
164 self.allow_input = True
165
166 def check_timed_events(self):
167 """
168 Check if amount of time has passed for timed events.
169 """
170 timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
171 c.ENEMY_HIT,
172 c.ENEMY_DEAD]
173 long_delay = timed_states[1:]
174
175 if self.state in long_delay:
176 if (self.current_time - self.timer) > 600:
177 if self.state == c.ENEMY_HIT:
178 self.state = c.ENEMY_DEAD
179 elif self.state == c.ENEMY_DEAD:
180 if len(self.enemy_list):
181 self.state = c.ENEMY_ATTACK
182 else:
183 self.state = c.BATTLE_WON
184 self.timer = self.current_time
185 self.notify(self.state)
186
187 elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
188 if (self.current_time - self.timer) > 600:
189 if self.enemy_index == (len(self.enemy_list) - 1):
190 self.state = c.SELECT_ACTION
191 else:
192 self.state = c.SWITCH_ENEMY
193 self.timer = self.current_time
194 self.notify(self.state)
195
196
197
198
199
200
201
202 def check_if_battle_won(self):
203 """
204 Check if state is SELECT_ACTION and there are no enemies left.
205 """
206 if self.state == c.SELECT_ACTION:
207 if len(self.enemy_group) == 0:
208 self.notify(c.BATTLE_WON)
209
210 def notify(self, event):
211 """
212 Notify observer of event.
213 """
214 for new_observer in self.observers:
215 new_observer.on_notify(event)
216
217 def end_battle(self):
218 """
219 End battle and flip back to previous state.
220 """
221 self.game_data['last state'] = self.name
222 self.done = True
223
224 def attack_enemy(self, enemy_damage):
225 enemy = self.player.attacked_enemy
226 enemy.health -= enemy_damage
227 self.set_enemy_indices()
228
229 if enemy:
230 if enemy.health <= 0:
231 enemy.kill()
232 self.enemy_list.pop(enemy.index)
233 self.arrow.remove_pos(self.player.attacked_enemy)
234 self.enemy_index = 0
235 posx = enemy.rect.x - 32
236 posy = enemy.rect.y - 64
237 fire_sprite = attack.Fire(posx, posy)
238 self.attack_animations.add(fire_sprite)
239 self.player.attacked_enemy = None
240
241 def set_enemy_indices(self):
242 for i, enemy in enumerate(self.enemy_list):
243 enemy.index = i
244
245 def draw_battle(self, surface):
246 """Draw all elements of battle state"""
247 self.background.draw(surface)
248 self.enemy_group.draw(surface)
249 self.attack_animations.draw(surface)
250 self.sword.draw(surface)
251 surface.blit(self.player.image, self.player.rect)
252 surface.blit(self.info_box.image, self.info_box.rect)
253 surface.blit(self.select_box.image, self.select_box.rect)
254 surface.blit(self.arrow.image, self.arrow.rect)
255 self.player_health_box.draw(surface)
256 self.damage_points.draw(surface)
257
258
259 def player_damaged(self, damage):
260 self.game_data['player stats']['health']['current'] -= damage
261
262 def set_timer_to_current_time(self):
263 """Set the timer to the current time."""
264 self.timer = self.current_time
265
266