data/states/battle.py (view raw)
1"""This is the state that handles battles against
2monsters"""
3import random, copy
4import pygame as pg
5from .. import tools, battlegui, observer
6from .. components import person, attack, attackitems
7from .. import constants as c
8
9
10class Battle(tools._State):
11 def __init__(self):
12 super(Battle, self).__init__()
13 self.game_data = {}
14 self.current_time = 0.0
15 self.timer = 0.0
16 self.allow_input = False
17 self.allow_info_box_change = False
18 self.name = 'battle'
19 self.state = None
20
21 self.player = None
22 self.attack_animations = None
23 self.sword = None
24 self.enemy_index = 0
25 self.attacked_enemy = None
26 self.attacking_enemy = None
27 self.enemy_group = None
28 self.enemy_pos_list = []
29 self.enemy_list = []
30
31 self.background = None
32 self.info_box = None
33 self.arrow = None
34 self.select_box = None
35 self.player_health_box = None
36 self.select_action_state_dict = {}
37 self.next = None
38 self.observers = []
39 self.damage_points = pg.sprite.Group()
40
41 def startup(self, current_time, game_data):
42 """Initialize state attributes"""
43 self.current_time = current_time
44 self.timer = current_time
45 self.game_data = game_data
46 self.inventory = game_data['player inventory']
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 self.damage_points = pg.sprite.Group()
66
67 @staticmethod
68 def make_background():
69 """Make the blue/black background"""
70 background = pg.sprite.Sprite()
71 surface = pg.Surface(c.SCREEN_SIZE).convert()
72 surface.fill(c.BLACK_BLUE)
73 background.image = surface
74 background.rect = background.image.get_rect()
75 background_group = pg.sprite.Group(background)
76
77 return background_group
78
79 @staticmethod
80 def make_enemies():
81 """Make the enemies for the battle. Return sprite group"""
82 pos_list = []
83
84 for column in range(3):
85 for row in range(3):
86 x = (column * 100) + 100
87 y = (row * 100) + 100
88 pos_list.append([x, y])
89
90 enemy_group = pg.sprite.Group()
91
92 for enemy in range(random.randint(1, 6)):
93 enemy_group.add(person.Person('devil', 0, 0,
94 'down', 'battle resting'))
95
96 for i, enemy in enumerate(enemy_group):
97 enemy.rect.topleft = pos_list[i]
98 enemy.image = pg.transform.scale2x(enemy.image)
99 enemy.index = i
100 enemy.level = 1
101 enemy.health = enemy.level * 7
102
103 enemy_list = [enemy for enemy in enemy_group]
104
105 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
106
107 @staticmethod
108 def make_player():
109 """Make the sprite for the player's character"""
110 player = person.Player('left', 630, 220, 'battle resting', 1)
111 player.image = pg.transform.scale2x(player.image)
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:
157 if self.arrow.index == (len(self.arrow.pos_list) - 1):
158 self.state = c.SELECT_ACTION
159 self.notify(self.state)
160 elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
161 self.state = c.DRINK_HEALING_POTION
162 self.notify(self.state)
163
164 elif self.state == c.SELECT_MAGIC:
165 if self.arrow.index == (len(self.arrow.pos_list) - 1):
166 self.state = c.SELECT_ACTION
167 self.notify(self.state)
168 elif self.info_box.magic_text_list[self.arrow.index] == 'Cure':
169 if self.game_data['player stats']['magic points']['current'] >= 25:
170 self.state = c.CURE_SPELL
171 self.notify(self.state)
172 elif self.info_box.magic_text_list[self.arrow.index] == 'Fire Blast':
173 if self.game_data['player stats']['magic points']['current'] >= 75:
174 self.state = c.FIRE_SPELL
175 self.notify(self.state)
176
177 self.allow_input = False
178
179 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
180 self.allow_input = True
181
182 def check_timed_events(self):
183 """
184 Check if amount of time has passed for timed events.
185 """
186 timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
187 c.ENEMY_HIT,
188 c.ENEMY_DEAD,
189 c.DRINK_HEALING_POTION]
190 long_delay = timed_states[1:]
191
192 if self.state in long_delay:
193 if (self.current_time - self.timer) > 800:
194 if self.state == c.ENEMY_HIT:
195 if len(self.enemy_list):
196 self.state = c.ENEMY_ATTACK
197 else:
198 self.state = c.BATTLE_WON
199 elif (self.state == c.DRINK_HEALING_POTION or
200 self.state == c.CURE_SPELL):
201 if len(self.enemy_list):
202 self.state = c.ENEMY_ATTACK
203 else:
204 self.state = c.BATTLE_WON
205 self.timer = self.current_time
206 self.notify(self.state)
207
208 elif self.state == c.FIRE_SPELL or self.state == c.CURE_SPELL:
209 if (self.current_time - self.timer) > 1500:
210 if len(self.enemy_list):
211 self.state = c.ENEMY_ATTACK
212 else:
213 self.state = c.BATTLE_WON
214 self.timer = self.current_time
215 self.notify(self.state)
216
217 elif self.state == c.RUN_AWAY:
218 if (self.current_time - self.timer) > 1500:
219 self.end_battle()
220
221 elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
222 if (self.current_time - self.timer) > 600:
223 if self.enemy_index == (len(self.enemy_list) - 1):
224 self.state = c.SELECT_ACTION
225 else:
226 self.state = c.SWITCH_ENEMY
227 self.timer = self.current_time
228 self.notify(self.state)
229
230 def check_if_battle_won(self):
231 """
232 Check if state is SELECT_ACTION and there are no enemies left.
233 """
234 if self.state == c.SELECT_ACTION:
235 if len(self.enemy_group) == 0:
236 self.notify(c.BATTLE_WON)
237
238 def notify(self, event):
239 """
240 Notify observer of event.
241 """
242 for new_observer in self.observers:
243 new_observer.on_notify(event)
244
245 def end_battle(self):
246 """
247 End battle and flip back to previous state.
248 """
249 self.game_data['last state'] = self.name
250 self.done = True
251
252 def attack_enemy(self, enemy_damage):
253 enemy = self.player.attacked_enemy
254 enemy.health -= enemy_damage
255 self.set_enemy_indices()
256
257 if enemy:
258 if enemy.health <= 0:
259 enemy.kill()
260 self.enemy_list.pop(enemy.index)
261 self.arrow.remove_pos(self.player.attacked_enemy)
262 posx = enemy.rect.x - 32
263 posy = enemy.rect.y - 64
264 fire_sprite = attack.Fire(posx, posy)
265 self.attack_animations.add(fire_sprite)
266 self.enemy_index = 0
267 self.player.attacked_enemy = None
268
269 def set_enemy_indices(self):
270 for i, enemy in enumerate(self.enemy_list):
271 enemy.index = i
272
273 def draw_battle(self, surface):
274 """Draw all elements of battle state"""
275 self.background.draw(surface)
276 self.enemy_group.draw(surface)
277 self.attack_animations.draw(surface)
278 self.sword.draw(surface)
279 surface.blit(self.player.image, self.player.rect)
280 surface.blit(self.info_box.image, self.info_box.rect)
281 surface.blit(self.select_box.image, self.select_box.rect)
282 surface.blit(self.arrow.image, self.arrow.rect)
283 self.player_health_box.draw(surface)
284 self.damage_points.draw(surface)
285
286
287 def player_damaged(self, damage):
288 self.game_data['player stats']['health']['current'] -= damage
289
290 def player_healed(self, heal, magic_points):
291 health = self.game_data['player stats']['health']
292
293 health['current'] += heal
294 if health['current'] > health['maximum']:
295 health['current'] = health['maximum']
296
297 if self.state == c.DRINK_HEALING_POTION:
298 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
299 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
300 del self.game_data['player inventory']['Healing Potion']
301 elif self.state == c.CURE_SPELL:
302 self.game_data['player stats']['magic points']['current'] -= magic_points
303
304 def set_timer_to_current_time(self):
305 """Set the timer to the current time."""
306 self.timer = self.current_time
307
308 def cast_fire_blast(self):
309 """
310 Cast fire blast on all enemies.
311 """
312 POWER = self.inventory['Fire Blast']['power']
313 MAGIC_POINTS = self.inventory['Fire Blast']['magic points']
314 self.game_data['player stats']['magic points']['current'] -= MAGIC_POINTS
315 for enemy in self.enemy_list:
316 DAMAGE = random.randint(POWER//2, POWER)
317 self.damage_points.add(
318 attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
319 enemy.health -= DAMAGE
320 posx = enemy.rect.x - 32
321 posy = enemy.rect.y - 64
322 fire_sprite = attack.Fire(posx, posy)
323 self.attack_animations.add(fire_sprite)
324 if enemy.health <= 0:
325 enemy.kill()
326 self.arrow.remove_pos(enemy)
327 self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
328 self.enemy_index = 0
329 self.arrow.index = 0
330 self.arrow.become_invisible_surface()
331 self.arrow.state = c.SELECT_ACTION
332 self.state = c.FIRE_SPELL
333 self.set_timer_to_current_time()
334 self.info_box.state = c.FIRE_SPELL
335
336 def cast_cure(self):
337 """
338 Cast cure spell on player.
339 """
340 HEAL_AMOUNT = self.inventory['Cure']['power']
341 MAGIC_POINTS = self.inventory['Cure']['magic points']
342 self.player.healing = True
343 self.set_timer_to_current_time()
344 self.state = c.CURE_SPELL
345 self.arrow.become_invisible_surface()
346 self.enemy_index = 0
347 self.damage_points.add(
348 attackitems.HealthPoints(HEAL_AMOUNT, self.player.rect.topright, False))
349 self.player_healed(HEAL_AMOUNT, MAGIC_POINTS)
350 self.info_box.state = c.DRINK_HEALING_POTION
351
352
353
354
355