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) > 800:
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 enemy.kill()
270 self.enemy_list.pop(enemy.index)
271 self.arrow.remove_pos(self.player.attacked_enemy)
272 posx = enemy.rect.x - 32
273 posy = enemy.rect.y - 64
274 fire_sprite = attack.Fire(posx, posy)
275 self.attack_animations.add(fire_sprite)
276 self.enemy_index = 0
277 self.player.attacked_enemy = None
278
279 def set_enemy_indices(self):
280 for i, enemy in enumerate(self.enemy_list):
281 enemy.index = i
282
283 def draw_battle(self, surface):
284 """Draw all elements of battle state"""
285 self.background.draw(surface)
286 self.enemy_group.draw(surface)
287 self.attack_animations.draw(surface)
288 self.sword.draw(surface)
289 surface.blit(self.player.image, self.player.rect)
290 surface.blit(self.info_box.image, self.info_box.rect)
291 surface.blit(self.select_box.image, self.select_box.rect)
292 surface.blit(self.arrow.image, self.arrow.rect)
293 self.player_health_box.draw(surface)
294 self.damage_points.draw(surface)
295
296
297 def player_damaged(self, damage):
298 self.game_data['player stats']['health']['current'] -= damage
299
300 def player_healed(self, heal, magic_points=0):
301 """
302 Add health from potion to game data.
303 """
304 health = self.game_data['player stats']['health']
305
306 health['current'] += heal
307 if health['current'] > health['maximum']:
308 health['current'] = health['maximum']
309
310 if self.state == c.DRINK_HEALING_POTION:
311 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
312 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
313 del self.game_data['player inventory']['Healing Potion']
314 elif self.state == c.CURE_SPELL:
315 self.game_data['player stats']['magic points']['current'] -= magic_points
316
317 def magic_boost(self, magic_points):
318 """
319 Add magic from ether to game data.
320 """
321 magic = self.game_data['player stats']['magic points']
322 magic['current'] += magic_points
323 if magic['current'] > magic['maximum']:
324 magic['current'] = magic['maximum']
325
326 self.game_data['player inventory']['Ether Potion']['quantity'] -= 1
327 if not self.game_data['player inventory']['Ether Potion']['quantity']:
328 del self.game_data['player inventory']['Ether Potion']
329
330 def set_timer_to_current_time(self):
331 """Set the timer to the current time."""
332 self.timer = self.current_time
333
334 def cast_fire_blast(self):
335 """
336 Cast fire blast on all enemies.
337 """
338 POWER = self.inventory['Fire Blast']['power']
339 MAGIC_POINTS = self.inventory['Fire Blast']['magic points']
340 self.game_data['player stats']['magic points']['current'] -= MAGIC_POINTS
341 for enemy in self.enemy_list:
342 DAMAGE = random.randint(POWER//2, POWER)
343 self.damage_points.add(
344 attackitems.HealthPoints(DAMAGE, enemy.rect.topright))
345 enemy.health -= DAMAGE
346 posx = enemy.rect.x - 32
347 posy = enemy.rect.y - 64
348 fire_sprite = attack.Fire(posx, posy)
349 self.attack_animations.add(fire_sprite)
350 if enemy.health <= 0:
351 enemy.kill()
352 self.arrow.remove_pos(enemy)
353 else:
354 enemy.enter_knock_back_state()
355 self.enemy_list = [enemy for enemy in self.enemy_list if enemy.health > 0]
356 self.enemy_index = 0
357 self.arrow.index = 0
358 self.arrow.become_invisible_surface()
359 self.arrow.state = c.SELECT_ACTION
360 self.state = c.FIRE_SPELL
361 self.set_timer_to_current_time()
362 self.info_box.state = c.FIRE_SPELL
363
364 def cast_cure(self):
365 """
366 Cast cure spell on player.
367 """
368 HEAL_AMOUNT = self.inventory['Cure']['power']
369 MAGIC_POINTS = self.inventory['Cure']['magic points']
370 self.player.healing = True
371 self.set_timer_to_current_time()
372 self.state = c.CURE_SPELL
373 self.arrow.become_invisible_surface()
374 self.enemy_index = 0
375 self.damage_points.add(
376 attackitems.HealthPoints(HEAL_AMOUNT, self.player.rect.topright, False))
377 self.player_healed(HEAL_AMOUNT, MAGIC_POINTS)
378 self.info_box.state = c.DRINK_HEALING_POTION
379
380 def drink_ether(self):
381 """
382 Drink ether potion.
383 """
384 self.player.healing = True
385 self.set_timer_to_current_time()
386 self.state = c.DRINK_ETHER_POTION
387 self.arrow.become_invisible_surface()
388 self.enemy_index = 0
389 self.damage_points.add(
390 attackitems.HealthPoints(30,
391 self.player.rect.topright,
392 False,
393 True))
394 self.magic_boost(30)
395 self.info_box.state = c.DRINK_ETHER_POTION
396
397 def drink_healing_potion(self):
398 """
399 Drink Healing Potion.
400 """
401 self.player.healing = True
402 self.set_timer_to_current_time()
403 self.state = c.DRINK_HEALING_POTION
404 self.arrow.become_invisible_surface()
405 self.enemy_index = 0
406 self.damage_points.add(
407 attackitems.HealthPoints(30,
408 self.player.rect.topright,
409 False))
410 self.player_healed(30)
411 self.info_box.state = c.DRINK_HEALING_POTION
412
413
414
415
416