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.allow_input = False
46 self.game_data = game_data
47 self.inventory = game_data['player inventory']
48 self.state = c.SELECT_ACTION
49 self.next = game_data['last state']
50 self.run_away = False
51
52 self.player = self.make_player()
53 self.attack_animations = pg.sprite.Group()
54 self.sword = attackitems.Sword(self.player)
55 self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
56 self.background = self.make_background()
57 self.info_box = battlegui.InfoBox(game_data)
58 self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
59 self.info_box)
60 self.select_box = battlegui.SelectBox()
61 self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
62 self.game_data)
63
64 self.select_action_state_dict = self.make_selection_state_dict()
65 self.observers = [observer.Battle(self)]
66 self.player.observers.extend(self.observers)
67 self.damage_points = pg.sprite.Group()
68
69 @staticmethod
70 def make_background():
71 """Make the blue/black background"""
72 background = pg.sprite.Sprite()
73 surface = pg.Surface(c.SCREEN_SIZE).convert()
74 surface.fill(c.BLACK_BLUE)
75 background.image = surface
76 background.rect = background.image.get_rect()
77 background_group = pg.sprite.Group(background)
78
79 return background_group
80
81 def make_enemies(self):
82 """Make the enemies for the battle. Return sprite group"""
83 pos_list = []
84
85 for column in range(3):
86 for row in range(3):
87 x = (column * 100) + 100
88 y = (row * 100) + 100
89 pos_list.append([x, y])
90
91 enemy_group = pg.sprite.Group()
92
93 if self.game_data['battle type']:
94 enemy = person.Person('evilwizard', 0, 0,
95 'down', 'battle resting')
96 enemy_group.add(enemy)
97 else:
98 for enemy in range(random.randint(1, 6)):
99 enemy_group.add(person.Person('devil', 0, 0,
100 'down', 'battle resting'))
101
102 for i, enemy in enumerate(enemy_group):
103 enemy.rect.topleft = pos_list[i]
104 enemy.image = pg.transform.scale2x(enemy.image)
105 enemy.index = i
106 enemy.level = 1
107 enemy.health = enemy.level * 7
108
109 enemy_list = [enemy for enemy in enemy_group]
110
111 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
112
113 @staticmethod
114 def make_player():
115 """Make the sprite for the player's character"""
116 player = person.Player('left', 630, 220, 'battle resting', 1)
117 player.image = pg.transform.scale2x(player.image)
118 return player
119
120 def make_selection_state_dict(self):
121 """
122 Make a dictionary of states with arrow coordinates as keys.
123 """
124 pos_list = self.arrow.make_select_action_pos_list()
125 state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
126 return dict(zip(pos_list, state_list))
127
128 def update(self, surface, keys, current_time):
129 """Update the battle state"""
130 self.current_time = current_time
131 self.check_input(keys)
132 self.check_timed_events()
133 self.check_if_battle_won()
134 self.enemy_group.update(current_time)
135 self.player.update(keys, current_time)
136 self.attack_animations.update()
137 self.info_box.update()
138 self.arrow.update(keys)
139 self.sword.update(current_time)
140 self.damage_points.update()
141
142 self.draw_battle(surface)
143
144 def check_input(self, keys):
145 """
146 Check user input to navigate GUI.
147 """
148 if self.allow_input:
149 if keys[pg.K_RETURN]:
150 self.notify(c.END_BATTLE)
151
152 elif keys[pg.K_SPACE]:
153 if self.state == c.SELECT_ACTION:
154 self.state = self.select_action_state_dict[
155 self.arrow.rect.topleft]
156 self.notify(self.state)
157
158 elif self.state == c.SELECT_ENEMY:
159 self.state = c.PLAYER_ATTACK
160 self.notify(self.state)
161
162 elif self.state == c.SELECT_ITEM:
163 if self.arrow.index == (len(self.arrow.pos_list) - 1):
164 self.state = c.SELECT_ACTION
165 self.notify(self.state)
166 elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
167 self.state = c.DRINK_HEALING_POTION
168 self.notify(self.state)
169 elif self.info_box.item_text_list[self.arrow.index][:5] == 'Ether':
170 self.state = c.DRINK_ETHER_POTION
171 self.notify(self.state)
172 elif self.state == c.SELECT_MAGIC:
173 if self.arrow.index == (len(self.arrow.pos_list) - 1):
174 self.state = c.SELECT_ACTION
175 self.notify(self.state)
176 elif self.info_box.magic_text_list[self.arrow.index] == 'Cure':
177 if self.game_data['player stats']['magic points']['current'] >= 25:
178 self.state = c.CURE_SPELL
179 self.notify(self.state)
180 elif self.info_box.magic_text_list[self.arrow.index] == 'Fire Blast':
181 if self.game_data['player stats']['magic points']['current'] >= 25:
182 self.state = c.FIRE_SPELL
183 self.notify(self.state)
184
185 self.allow_input = False
186
187 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
188 self.allow_input = True
189
190 def check_timed_events(self):
191 """
192 Check if amount of time has passed for timed events.
193 """
194 timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
195 c.ENEMY_HIT,
196 c.ENEMY_DEAD,
197 c.DRINK_HEALING_POTION,
198 c.DRINK_ETHER_POTION]
199 long_delay = timed_states[1:]
200
201 if self.state in long_delay:
202 if (self.current_time - self.timer) > 1000:
203 if self.state == c.ENEMY_HIT:
204 if len(self.enemy_list):
205 self.state = c.ENEMY_ATTACK
206 else:
207 self.state = c.BATTLE_WON
208 elif (self.state == c.DRINK_HEALING_POTION or
209 self.state == c.CURE_SPELL or
210 self.state == c.DRINK_ETHER_POTION):
211 if len(self.enemy_list):
212 self.state = c.ENEMY_ATTACK
213 else:
214 self.state = c.BATTLE_WON
215 self.timer = self.current_time
216 self.notify(self.state)
217
218 elif self.state == c.FIRE_SPELL or self.state == c.CURE_SPELL:
219 if (self.current_time - self.timer) > 1500:
220 if len(self.enemy_list):
221 self.state = c.ENEMY_ATTACK
222 else:
223 self.state = c.BATTLE_WON
224 self.timer = self.current_time
225 self.notify(self.state)
226
227 elif self.state == c.FLEE or self.state == c.BATTLE_WON:
228 if (self.current_time - self.timer) > 1500:
229 self.end_battle()
230
231 elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
232 if (self.current_time - self.timer) > 600:
233 if self.enemy_index == (len(self.enemy_list) - 1):
234 if self.run_away:
235 self.state = c.FLEE
236 else:
237 self.state = c.SELECT_ACTION
238 else:
239 self.state = c.SWITCH_ENEMY
240 self.timer = self.current_time
241 self.notify(self.state)
242
243 def check_if_battle_won(self):
244 """
245 Check if state is SELECT_ACTION and there are no enemies left.
246 """
247 if self.state == c.SELECT_ACTION:
248 if len(self.enemy_group) == 0:
249 self.notify(c.BATTLE_WON)
250
251 def notify(self, event):
252 """
253 Notify observer of event.
254 """
255 for new_observer in self.observers:
256 new_observer.on_notify(event)
257
258 def end_battle(self):
259 """
260 End battle and flip back to previous state.
261 """
262 if self.game_data['battle type'] == 'evilwizard':
263 self.game_data['crown quest'] = True
264 self.game_data['last state'] = self.name
265 self.game_data['battle counter'] = random.randint(50, 255)
266 self.game_data['battle type'] = None
267 self.done = True
268
269 def attack_enemy(self, enemy_damage):
270 enemy = self.player.attacked_enemy
271 enemy.health -= enemy_damage
272 self.set_enemy_indices()
273
274 if enemy:
275 enemy.enter_knock_back_state()
276 if enemy.health <= 0:
277 self.enemy_list.pop(enemy.index)
278 enemy.state = c.FADE_DEATH
279 self.notify(c.FADE_DEATH)
280 self.arrow.remove_pos(self.player.attacked_enemy)
281 self.enemy_index = 0
282
283 def set_enemy_indices(self):
284 for i, enemy in enumerate(self.enemy_list):
285 enemy.index = i
286
287 def draw_battle(self, surface):
288 """Draw all elements of battle state"""
289 self.background.draw(surface)
290 self.enemy_group.draw(surface)
291 self.attack_animations.draw(surface)
292 self.sword.draw(surface)
293 surface.blit(self.player.image, self.player.rect)
294 surface.blit(self.info_box.image, self.info_box.rect)
295 surface.blit(self.select_box.image, self.select_box.rect)
296 surface.blit(self.arrow.image, self.arrow.rect)
297 self.player_health_box.draw(surface)
298 self.damage_points.draw(surface)
299
300
301 def player_damaged(self, damage):
302 self.game_data['player stats']['health']['current'] -= damage
303
304 def player_healed(self, heal, magic_points=0):
305 """
306 Add health from potion to game data.
307 """
308 health = self.game_data['player stats']['health']
309
310 health['current'] += heal
311 if health['current'] > health['maximum']:
312 health['current'] = health['maximum']
313
314 if self.state == c.DRINK_HEALING_POTION:
315 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
316 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
317 del self.game_data['player inventory']['Healing Potion']
318 elif self.state == c.CURE_SPELL:
319 self.game_data['player stats']['magic points']['current'] -= magic_points
320
321 def magic_boost(self, magic_points):
322 """
323 Add magic from ether to game data.
324 """
325 magic = self.game_data['player stats']['magic points']
326 magic['current'] += magic_points
327 if magic['current'] > magic['maximum']:
328 magic['current'] = magic['maximum']
329
330 self.game_data['player inventory']['Ether Potion']['quantity'] -= 1
331 if not self.game_data['player inventory']['Ether Potion']['quantity']:
332 del self.game_data['player inventory']['Ether Potion']
333
334 def set_timer_to_current_time(self):
335 """Set the timer to the current time."""
336 self.timer = self.current_time
337
338 def cast_fire_blast(self):
339 """
340 Cast fire blast on all enemies.
341 """
342 POWER = self.inventory['Fire Blast']['power']
343 MAGIC_POINTS = self.inventory['Fire Blast']['magic points']
344 self.game_data['player stats']['magic points']['current'] -= MAGIC_POINTS
345 for enemy in self.enemy_list:
346 DAMAGE = random.randint(POWER//2, POWER)
347 self.damage_points.add(
348 attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
349 enemy.health -= DAMAGE
350 posx = enemy.rect.x - 32
351 posy = enemy.rect.y - 64
352 fire_sprite = attack.Fire(posx, posy)
353 self.attack_animations.add(fire_sprite)
354 if enemy.health <= 0:
355 enemy.kill()
356 self.arrow.remove_pos(enemy)
357 else:
358 enemy.enter_knock_back_state()
359 self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
360 self.enemy_index = 0
361 self.arrow.index = 0
362 self.arrow.become_invisible_surface()
363 self.arrow.state = c.SELECT_ACTION
364 self.state = c.FIRE_SPELL
365 self.set_timer_to_current_time()
366 self.info_box.state = c.FIRE_SPELL
367
368 def cast_cure(self):
369 """
370 Cast cure spell on player.
371 """
372 HEAL_AMOUNT = self.inventory['Cure']['power']
373 MAGIC_POINTS = self.inventory['Cure']['magic points']
374 self.player.healing = True
375 self.set_timer_to_current_time()
376 self.state = c.CURE_SPELL
377 self.arrow.become_invisible_surface()
378 self.enemy_index = 0
379 self.damage_points.add(
380 attackitems.HealthPoints(HEAL_AMOUNT, self.player.rect.topright, False))
381 self.player_healed(HEAL_AMOUNT, MAGIC_POINTS)
382 self.info_box.state = c.DRINK_HEALING_POTION
383
384 def drink_ether(self):
385 """
386 Drink ether potion.
387 """
388 self.player.healing = True
389 self.set_timer_to_current_time()
390 self.state = c.DRINK_ETHER_POTION
391 self.arrow.become_invisible_surface()
392 self.enemy_index = 0
393 self.damage_points.add(
394 attackitems.HealthPoints(30,
395 self.player.rect.topright,
396 False,
397 True))
398 self.magic_boost(30)
399 self.info_box.state = c.DRINK_ETHER_POTION
400
401 def drink_healing_potion(self):
402 """
403 Drink Healing Potion.
404 """
405 self.player.healing = True
406 self.set_timer_to_current_time()
407 self.state = c.DRINK_HEALING_POTION
408 self.arrow.become_invisible_surface()
409 self.enemy_index = 0
410 self.damage_points.add(
411 attackitems.HealthPoints(30,
412 self.player.rect.topright,
413 False))
414 self.player_healed(30)
415 self.info_box.state = c.DRINK_HEALING_POTION
416
417
418
419
420