data/states/battle.py (view raw)
1"""This is the state that handles battles against
2monsters"""
3import random
4import pygame as pg
5from .. import tools, battlegui, observer, setup
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.name = 'battle'
14
15 def startup(self, current_time, game_data):
16 """Initialize state attributes"""
17 self.current_time = current_time
18 self.timer = current_time
19 self.allow_input = False
20 self.game_data = game_data
21 self.inventory = game_data['player inventory']
22 self.state = c.SELECT_ACTION
23 self.next = game_data['last state']
24 self.run_away = False
25
26 self.player = self.make_player()
27 self.attack_animations = pg.sprite.Group()
28 self.sword = attackitems.Sword(self.player)
29 self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
30 self.experience_points = self.get_experience_points()
31 self.background = self.make_background()
32 self.info_box = battlegui.InfoBox(game_data, self.experience_points)
33 self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
34 self.info_box)
35 self.select_box = battlegui.SelectBox()
36 self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
37 self.game_data)
38
39 self.select_action_state_dict = self.make_selection_state_dict()
40 self.observers = [observer.Battle(self)]
41 self.player.observers.extend(self.observers)
42 self.damage_points = pg.sprite.Group()
43
44 def make_enemy_level_dict(self):
45 new_dict = {c.OVERWORLD: 1,
46 c.DUNGEON: 2,
47 c.DUNGEON2: 2,
48 c.DUNGEON3: 3,
49 c.DUNGEON4: 2,
50 c.DUNGEON5: 5}
51
52 return new_dict
53
54 def set_enemy_level(self, enemy_list):
55 dungeon_level_dict = self.make_enemy_level_dict()
56
57 for enemy in enemy_list:
58 enemy.level = dungeon_level_dict[self.previous]
59
60 def get_experience_points(self):
61 """
62 Calculate experience points based on number of enemies
63 and their levels.
64 """
65 experience_total = 0
66
67 for enemy in self.enemy_list:
68 experience_total += (random.randint(5,10)*enemy.level)
69
70 return experience_total
71
72 def make_background(self):
73 """Make the blue/black background"""
74 background = pg.sprite.Sprite()
75 surface = pg.Surface(c.SCREEN_SIZE).convert()
76 surface.fill(c.BLACK_BLUE)
77 background.image = surface
78 background.rect = background.image.get_rect()
79 background_group = pg.sprite.Group(background)
80
81 return background_group
82
83 def make_enemies(self):
84 """Make the enemies for the battle. Return sprite group"""
85 pos_list = []
86
87 for column in range(3):
88 for row in range(3):
89 x = (column * 100) + 100
90 y = (row * 100) + 100
91 pos_list.append([x, y])
92
93 enemy_group = pg.sprite.Group()
94
95 if self.game_data['battle type']:
96 enemy = person.Enemy('evilwizard', 0, 0,
97 'down', 'battle resting')
98 enemy_group.add(enemy)
99 else:
100 for enemy in range(random.randint(1, 6)):
101 enemy_group.add(person.Enemy('devil', 0, 0,
102 'down', 'battle resting'))
103
104 for i, enemy in enumerate(enemy_group):
105 enemy.rect.topleft = pos_list[i]
106 enemy.image = pg.transform.scale2x(enemy.image)
107 enemy.index = i
108 enemy.level = self.make_enemy_level_dict()[self.previous]
109 enemy.health = enemy.level * 7
110
111 enemy_list = [enemy for enemy in enemy_group]
112
113 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
114
115 def make_player(self):
116 """Make the sprite for the player's character"""
117 player = person.Player('left', self.game_data, 630, 220, 'battle resting', 1)
118 player.image = pg.transform.scale2x(player.image)
119 return player
120
121 def make_selection_state_dict(self):
122 """
123 Make a dictionary of states with arrow coordinates as keys.
124 """
125 pos_list = self.arrow.make_select_action_pos_list()
126 state_list = [self.enter_select_enemy_state, self.enter_select_item_state,
127 self.enter_select_magic_state, self.try_to_run_away]
128 return dict(zip(pos_list, state_list))
129
130 def update(self, surface, keys, current_time):
131 """Update the battle state"""
132 self.current_time = current_time
133 self.check_input(keys)
134 self.check_timed_events()
135 self.check_if_battle_won()
136 self.enemy_group.update(current_time)
137 self.player.update(keys, current_time)
138 self.attack_animations.update()
139 self.info_box.update()
140 self.arrow.update(keys)
141 self.sword.update(current_time)
142 self.damage_points.update()
143
144 self.draw_battle(surface)
145
146 def check_input(self, keys):
147 """
148 Check user input to navigate GUI.
149 """
150 if self.allow_input:
151 if keys[pg.K_RETURN]:
152 self.end_battle()
153
154 elif keys[pg.K_SPACE]:
155 if self.state == c.SELECT_ACTION:
156 enter_state_function = self.select_action_state_dict[
157 self.arrow.rect.topleft]
158 enter_state_function()
159
160 elif self.state == c.SELECT_ENEMY:
161 self.enter_player_attack_state()
162
163 elif self.state == c.SELECT_ITEM:
164 if self.arrow.index == (len(self.arrow.pos_list) - 1):
165 self.enter_select_action_state()
166 elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
167 self.enter_drink_healing_potion_state()
168 elif self.info_box.item_text_list[self.arrow.index][:5] == 'Ether':
169 self.enter_drink_ether_potion_state()
170 elif self.state == c.SELECT_MAGIC:
171 if self.arrow.index == (len(self.arrow.pos_list) - 1):
172 self.enter_select_action_state()
173 elif self.info_box.magic_text_list[self.arrow.index] == 'Cure':
174 if self.game_data['player stats']['magic points']['current'] >= 25:
175 self.cast_cure()
176 elif self.info_box.magic_text_list[self.arrow.index] == 'Fire Blast':
177 if self.game_data['player stats']['magic points']['current'] >= 25:
178 self.cast_fire_blast()
179
180 self.allow_input = False
181
182 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
183 self.allow_input = True
184
185 def check_timed_events(self):
186 """
187 Check if amount of time has passed for timed events.
188 """
189 timed_states = [c.PLAYER_DAMAGED,
190 c.ENEMY_DAMAGED,
191 c.ENEMY_DEAD,
192 c.DRINK_HEALING_POTION,
193 c.DRINK_ETHER_POTION]
194 long_delay = timed_states[1:]
195
196 if self.state in long_delay:
197 if (self.current_time - self.timer) > 1000:
198 if self.state == c.ENEMY_DAMAGED:
199 if len(self.enemy_list):
200 self.enter_enemy_attack_state()
201 else:
202 self.enter_battle_won_state()
203 elif (self.state == c.DRINK_HEALING_POTION or
204 self.state == c.CURE_SPELL or
205 self.state == c.DRINK_ETHER_POTION):
206 if len(self.enemy_list):
207 self.enter_enemy_attack_state()
208 else:
209 self.enter_battle_won_state()
210 self.timer = self.current_time
211
212 elif self.state == c.FIRE_SPELL or self.state == c.CURE_SPELL:
213 if (self.current_time - self.timer) > 1500:
214 if len(self.enemy_list):
215 self.enter_enemy_attack_state()
216 else:
217 self.enter_battle_won_state()
218 self.timer = self.current_time
219
220 elif self.state == c.RUN_AWAY:
221 if (self.current_time - self.timer) > 1500:
222 self.end_battle()
223
224 elif self.state == c.BATTLE_WON:
225 if (self.current_time - self.timer) > 1800:
226 self.enter_show_experience_state()
227
228 elif self.state == c.LEVEL_UP:
229 if (self.current_time - self.timer) > 2200:
230 self.end_battle()
231
232 elif self.state == c.SHOW_EXPERIENCE:
233 if (self.current_time - self.timer) > 2200:
234 player_stats = self.game_data['player stats']
235 player_stats['experience to next level'] -= self.experience_points
236 if player_stats['experience to next level'] <= 0:
237 player_stats['Level'] += 1
238 player_stats['health']['maximum'] += int(player_stats['health']['maximum']*.25)
239 player_stats['magic points']['maximum'] += int(player_stats['magic points']['maximum']*.20)
240 new_experience = int((player_stats['Level'] * 100) * .75)
241 player_stats['experience to next level'] = new_experience
242 self.enter_level_up_state()
243 else:
244 self.end_battle()
245
246 elif self.state == c.PLAYER_DAMAGED:
247 if (self.current_time - self.timer) > 600:
248 if self.enemy_index == (len(self.enemy_list) - 1):
249 if self.run_away:
250 self.enter_run_away_state()
251 else:
252 self.enter_select_action_state()
253 else:
254 self.switch_enemy()
255 self.timer = self.current_time
256
257 def check_if_battle_won(self):
258 """
259 Check if state is SELECT_ACTION and there are no enemies left.
260 """
261 if self.state == c.SELECT_ACTION:
262 if len(self.enemy_group) == 0:
263 self.enter_battle_won_state()
264
265 def notify(self, event):
266 """
267 Notify observer of event.
268 """
269 for new_observer in self.observers:
270 new_observer.on_notify(event)
271
272 def end_battle(self):
273 """
274 End battle and flip back to previous state.
275 """
276 if self.game_data['battle type'] == 'evilwizard':
277 self.game_data['crown quest'] = True
278 self.game_data['last state'] = self.name
279 self.game_data['battle counter'] = random.randint(50, 255)
280 self.game_data['battle type'] = None
281 self.done = True
282
283 def attack_enemy(self, enemy_damage):
284 enemy = self.player.attacked_enemy
285 enemy.health -= enemy_damage
286 self.set_enemy_indices()
287
288 if enemy:
289 enemy.enter_knock_back_state()
290 if enemy.health <= 0:
291 self.enemy_list.pop(enemy.index)
292 enemy.state = c.FADE_DEATH
293 self.arrow.remove_pos(self.player.attacked_enemy)
294 self.enemy_index = 0
295
296 def set_enemy_indices(self):
297 for i, enemy in enumerate(self.enemy_list):
298 enemy.index = i
299
300 def draw_battle(self, surface):
301 """Draw all elements of battle state"""
302 self.background.draw(surface)
303 self.enemy_group.draw(surface)
304 self.attack_animations.draw(surface)
305 self.sword.draw(surface)
306 surface.blit(self.player.image, self.player.rect)
307 surface.blit(self.info_box.image, self.info_box.rect)
308 surface.blit(self.select_box.image, self.select_box.rect)
309 surface.blit(self.arrow.image, self.arrow.rect)
310 self.player_health_box.draw(surface)
311 self.damage_points.draw(surface)
312
313 def player_damaged(self, damage):
314 self.game_data['player stats']['health']['current'] -= damage
315
316 def player_healed(self, heal, magic_points=0):
317 """
318 Add health from potion to game data.
319 """
320 health = self.game_data['player stats']['health']
321
322 health['current'] += heal
323 if health['current'] > health['maximum']:
324 health['current'] = health['maximum']
325
326 if self.state == c.DRINK_HEALING_POTION:
327 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
328 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
329 del self.game_data['player inventory']['Healing Potion']
330 elif self.state == c.CURE_SPELL:
331 self.game_data['player stats']['magic points']['current'] -= magic_points
332
333 def magic_boost(self, magic_points):
334 """
335 Add magic from ether to game data.
336 """
337 magic = self.game_data['player stats']['magic points']
338 magic['current'] += magic_points
339 if magic['current'] > magic['maximum']:
340 magic['current'] = magic['maximum']
341
342 self.game_data['player inventory']['Ether Potion']['quantity'] -= 1
343 if not self.game_data['player inventory']['Ether Potion']['quantity']:
344 del self.game_data['player inventory']['Ether Potion']
345
346 def set_timer_to_current_time(self):
347 """Set the timer to the current time."""
348 self.timer = self.current_time
349
350 def cast_fire_blast(self):
351 """
352 Cast fire blast on all enemies.
353 """
354 self.state = self.info_box.state = c.FIRE_SPELL
355 POWER = self.inventory['Fire Blast']['power']
356 MAGIC_POINTS = self.inventory['Fire Blast']['magic points']
357 self.game_data['player stats']['magic points']['current'] -= MAGIC_POINTS
358 for enemy in self.enemy_list:
359 DAMAGE = random.randint(POWER//2, POWER)
360 self.damage_points.add(
361 attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
362 enemy.health -= DAMAGE
363 posx = enemy.rect.x - 32
364 posy = enemy.rect.y - 64
365 fire_sprite = attack.Fire(posx, posy)
366 self.attack_animations.add(fire_sprite)
367 if enemy.health <= 0:
368 enemy.kill()
369 self.arrow.remove_pos(enemy)
370 else:
371 enemy.enter_knock_back_state()
372 self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
373 self.enemy_index = 0
374 self.arrow.index = 0
375 self.arrow.become_invisible_surface()
376 self.arrow.state = c.SELECT_ACTION
377 self.set_timer_to_current_time()
378
379 def cast_cure(self):
380 """
381 Cast cure spell on player.
382 """
383 self.state = c.CURE_SPELL
384 HEAL_AMOUNT = self.inventory['Cure']['power']
385 MAGIC_POINTS = self.inventory['Cure']['magic points']
386 self.player.healing = True
387 self.set_timer_to_current_time()
388 self.arrow.become_invisible_surface()
389 self.enemy_index = 0
390 self.damage_points.add(
391 attackitems.HealthPoints(HEAL_AMOUNT, self.player.rect.topright, False))
392 self.player_healed(HEAL_AMOUNT, MAGIC_POINTS)
393 self.info_box.state = c.DRINK_HEALING_POTION
394
395 def drink_ether(self):
396 """
397 Drink ether potion.
398 """
399 self.state = self.info_box.state = c.DRINK_ETHER_POTION
400 self.player.healing = True
401 self.set_timer_to_current_time()
402 self.arrow.become_invisible_surface()
403 self.enemy_index = 0
404 self.damage_points.add(
405 attackitems.HealthPoints(30,
406 self.player.rect.topright,
407 False,
408 True))
409 self.magic_boost(30)
410
411 def drink_healing_potion(self):
412 """
413 Drink Healing Potion.
414 """
415 self.state = self.info_box.state = c.DRINK_HEALING_POTION
416 self.player.healing = True
417 self.arrow.become_invisible_surface()
418 self.enemy_index = 0
419 self.damage_points.add(
420 attackitems.HealthPoints(30,
421 self.player.rect.topright,
422 False))
423 self.player_healed(30)
424 self.set_timer_to_current_time()
425
426 def enter_select_enemy_state(self):
427 """
428 Transition battle into the select enemy state.
429 """
430 self.state = self.arrow.state = c.SELECT_ENEMY
431 self.arrow.index = 0
432
433 def enter_select_item_state(self):
434 """
435 Transition battle into the select item state.
436 """
437 self.state = self.info_box.state = c.SELECT_ITEM
438 self.arrow.become_select_item_state()
439
440 def enter_select_magic_state(self):
441 """
442 Transition battle into the select magic state.
443 """
444 self.state = self.info_box.state = c.SELECT_MAGIC
445 self.arrow.become_select_magic_state()
446
447 def try_to_run_away(self):
448 """
449 Transition battle into the run away state.
450 """
451 self.run_away = True
452 self.arrow.become_invisible_surface()
453 self.enemy_index = 0
454 self.enter_enemy_attack_state()
455
456 def enter_enemy_attack_state(self):
457 """
458 Transition battle into the Enemy attack state.
459 """
460 self.state = self.info_box.state = c.ENEMY_ATTACK
461 enemy = self.enemy_list[self.enemy_index]
462 enemy.enter_enemy_attack_state()
463
464 def enter_player_attack_state(self):
465 """
466 Transition battle into the Player attack state.
467 """
468 self.state = c.PLAYER_ATTACK
469 enemy_posx = self.arrow.rect.x + 60
470 enemy_posy = self.arrow.rect.y - 20
471 enemy_pos = (enemy_posx, enemy_posy)
472 enemy_to_attack = None
473
474 for enemy in self.enemy_list:
475 if enemy.rect.topleft == enemy_pos:
476 enemy_to_attack = enemy
477
478 self.player.enter_attack_state(enemy_to_attack)
479 self.arrow.become_invisible_surface()
480
481 def enter_drink_healing_potion_state(self):
482 """
483 Transition battle into the Drink Healing Potion state.
484 """
485 self.state = self.info_box.state = c.DRINK_HEALING_POTION
486 self.player.healing = True
487 self.set_timer_to_current_time()
488 self.arrow.become_invisible_surface()
489 self.enemy_index = 0
490 self.damage_points.add(
491 attackitems.HealthPoints(30,
492 self.player.rect.topright,
493 False))
494 self.player_healed(30)
495
496 def enter_drink_ether_potion_state(self):
497 """
498 Transition battle into the Drink Ether Potion state.
499 """
500 self.state = self.info_box.state = c.DRINK_ETHER_POTION
501 self.player.healing = True
502 self.arrow.become_invisible_surface()
503 self.enemy_index = 0
504 self.damage_points.add(
505 attackitems.HealthPoints(30,
506 self.player.rect.topright,
507 False,
508 True))
509 self.magic_boost(30)
510 self.set_timer_to_current_time()
511
512 def enter_select_action_state(self):
513 """
514 Transition battle into the select action state
515 """
516 self.state = self.info_box.state = c.SELECT_ACTION
517 self.arrow.index = 0
518 self.arrow.state = self.state
519 self.arrow.image = setup.GFX['smallarrow']
520
521 def enter_player_damaged_state(self):
522 """
523 Transition battle into the player damaged state.
524 """
525 self.state = self.info_box.state = c.PLAYER_DAMAGED
526 if self.enemy_index > len(self.enemy_list) - 1:
527 self.enemy_index = 0
528 enemy = self.enemy_list[self.enemy_index]
529 player_damage = enemy.calculate_hit(self.inventory['equipped armor'],
530 self.inventory)
531 self.damage_points.add(
532 attackitems.HealthPoints(player_damage,
533 self.player.rect.topright))
534 self.info_box.set_player_damage(player_damage)
535 self.set_timer_to_current_time()
536 self.player_damaged(player_damage)
537 if player_damage:
538 self.player.damaged = True
539 self.player.enter_knock_back_state()
540
541 def enter_enemy_damaged_state(self):
542 """
543 Transition battle into the enemy damaged state.
544 """
545 self.state = self.info_box.state = c.ENEMY_DAMAGED
546 enemy_damage = self.player.calculate_hit()
547 self.damage_points.add(
548 attackitems.HealthPoints(enemy_damage,
549 self.player.attacked_enemy.rect.topright))
550
551 self.info_box.set_enemy_damage(enemy_damage)
552
553 self.arrow.state = c.SELECT_ACTION
554 self.arrow.index = 0
555 self.attack_enemy(enemy_damage)
556 self.set_timer_to_current_time()
557
558 def switch_enemy(self):
559 """
560 Switch which enemy the player is attacking.
561 """
562 if self.enemy_index < len(self.enemy_list) - 1:
563 self.enemy_index += 1
564 self.enter_enemy_attack_state()
565
566 def enter_run_away_state(self):
567 """
568 Transition battle into the run away state.
569 """
570 self.state = self.info_box.state = c.RUN_AWAY
571 self.arrow.become_invisible_surface()
572 self.player.state = c.RUN_AWAY
573 self.set_timer_to_current_time()
574
575 def enter_battle_won_state(self):
576 """
577 Transition battle into the battle won state.
578 """
579 self.state = self.info_box.state = c.BATTLE_WON
580 self.player.state = c.VICTORY_DANCE
581 self.set_timer_to_current_time()
582
583 def enter_show_experience_state(self):
584 """
585 Transition battle into the show experience state.
586 """
587 self.state = self.info_box.state = c.SHOW_EXPERIENCE
588 self.set_timer_to_current_time()
589
590 def enter_level_up_state(self):
591 """
592 Transition battle into the LEVEL UP state.
593 """
594 self.state = self.info_box.state = c.LEVEL_UP
595 self.info_box.reset_level_up_message()
596 self.set_timer_to_current_time()
597
598
599
600
601
602