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