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.state = c.SELECT_ACTION
47 self.next = game_data['last state']
48
49 self.player = self.make_player()
50 self.attack_animations = pg.sprite.Group()
51 self.sword = attackitems.Sword(self.player)
52 self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
53 self.background = self.make_background()
54 self.info_box = battlegui.InfoBox(game_data)
55 self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
56 self.info_box)
57 self.select_box = battlegui.SelectBox()
58 self.player_health_box = battlegui.PlayerHealth(self.select_box.rect,
59 self.game_data)
60
61 self.select_action_state_dict = self.make_selection_state_dict()
62 self.observers = [observer.Battle(self)]
63 self.player.observers.extend(self.observers)
64
65 @staticmethod
66 def make_background():
67 """Make the blue/black background"""
68 background = pg.sprite.Sprite()
69 surface = pg.Surface(c.SCREEN_SIZE).convert()
70 surface.fill(c.BLACK_BLUE)
71 background.image = surface
72 background.rect = background.image.get_rect()
73 background_group = pg.sprite.Group(background)
74
75 return background_group
76
77 @staticmethod
78 def make_enemies():
79 """Make the enemies for the battle. Return sprite group"""
80 pos_list = []
81
82 for column in range(3):
83 for row in range(3):
84 x = (column * 100) + 100
85 y = (row * 100) + 100
86 pos_list.append([x, y])
87
88 enemy_group = pg.sprite.Group()
89
90 for enemy in range(random.randint(1, 6)):
91 enemy_group.add(person.Person('devil', 0, 0,
92 'down', 'battle resting'))
93
94 for i, enemy in enumerate(enemy_group):
95 enemy.rect.topleft = pos_list[i]
96 enemy.image = pg.transform.scale2x(enemy.image)
97 enemy.index = i
98 enemy.level = 1
99 enemy.health = enemy.level * 7
100
101 enemy_list = [enemy for enemy in enemy_group]
102
103 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
104
105 @staticmethod
106 def make_player():
107 """Make the sprite for the player's character"""
108 player = person.Player('left', 630, 220, 'battle resting', 1)
109 player.image = pg.transform.scale2x(player.image)
110 return player
111
112 def make_selection_state_dict(self):
113 """
114 Make a dictionary of states with arrow coordinates as keys.
115 """
116 pos_list = self.arrow.make_select_action_pos_list()
117 state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
118 return dict(zip(pos_list, state_list))
119
120 def update(self, surface, keys, current_time):
121 """Update the battle state"""
122 self.current_time = current_time
123 self.check_input(keys)
124 self.check_timed_events()
125 self.check_if_battle_won()
126 self.enemy_group.update(current_time)
127 self.player.update(keys, current_time)
128 self.attack_animations.update()
129 self.info_box.update()
130 self.arrow.update(keys)
131 self.sword.update(current_time)
132 self.damage_points.update()
133
134 self.draw_battle(surface)
135
136 def check_input(self, keys):
137 """
138 Check user input to navigate GUI.
139 """
140 if self.allow_input:
141 if keys[pg.K_RETURN]:
142 self.notify(c.END_BATTLE)
143
144 elif keys[pg.K_SPACE]:
145 if self.state == c.SELECT_ACTION:
146 self.state = self.select_action_state_dict[
147 self.arrow.rect.topleft]
148 self.notify(self.state)
149
150 elif self.state == c.SELECT_ENEMY:
151 self.state = c.PLAYER_ATTACK
152 self.notify(self.state)
153
154 elif self.state == c.SELECT_ITEM:
155 if self.arrow.index == (len(self.arrow.pos_list) - 1):
156 self.state = c.SELECT_ACTION
157 self.notify(self.state)
158 elif self.info_box.item_text_list[self.arrow.index][:14] == 'Healing Potion':
159 self.state = c.DRINK_HEALING_POTION
160 self.notify(self.state)
161
162 elif self.state == c.SELECT_MAGIC:
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.magic_text_list[self.arrow.index] == 'Cure':
167 self.state = c.CURE_SPELL
168 self.notify(self.state)
169
170 self.allow_input = False
171
172 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
173 self.allow_input = True
174
175 def check_timed_events(self):
176 """
177 Check if amount of time has passed for timed events.
178 """
179 timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
180 c.ENEMY_HIT,
181 c.ENEMY_DEAD,
182 c.DRINK_HEALING_POTION,
183 c.CURE_SPELL]
184 long_delay = timed_states[1:]
185
186 if self.state in long_delay:
187 if (self.current_time - self.timer) > 600:
188 if self.state == c.ENEMY_HIT:
189 self.state = c.ENEMY_DEAD
190 elif self.state == c.ENEMY_DEAD:
191 if len(self.enemy_list):
192 self.state = c.ENEMY_ATTACK
193 else:
194 self.state = c.BATTLE_WON
195 elif self.state == c.DRINK_HEALING_POTION or self.state == c.CURE_SPELL:
196 self.state = c.ENEMY_ATTACK
197 self.timer = self.current_time
198 self.notify(self.state)
199
200 elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
201 if (self.current_time - self.timer) > 600:
202 if self.enemy_index == (len(self.enemy_list) - 1):
203 self.state = c.SELECT_ACTION
204 else:
205 self.state = c.SWITCH_ENEMY
206 self.timer = self.current_time
207 self.notify(self.state)
208
209 def check_if_battle_won(self):
210 """
211 Check if state is SELECT_ACTION and there are no enemies left.
212 """
213 if self.state == c.SELECT_ACTION:
214 if len(self.enemy_group) == 0:
215 self.notify(c.BATTLE_WON)
216
217 def notify(self, event):
218 """
219 Notify observer of event.
220 """
221 for new_observer in self.observers:
222 new_observer.on_notify(event)
223
224 def end_battle(self):
225 """
226 End battle and flip back to previous state.
227 """
228 self.game_data['last state'] = self.name
229 self.done = True
230
231 def attack_enemy(self, enemy_damage):
232 enemy = self.player.attacked_enemy
233 enemy.health -= enemy_damage
234 self.set_enemy_indices()
235
236 if enemy:
237 if enemy.health <= 0:
238 enemy.kill()
239 self.enemy_list.pop(enemy.index)
240 self.arrow.remove_pos(self.player.attacked_enemy)
241 posx = enemy.rect.x - 32
242 posy = enemy.rect.y - 64
243 fire_sprite = attack.Fire(posx, posy)
244 self.attack_animations.add(fire_sprite)
245 self.enemy_index = 0
246 self.player.attacked_enemy = None
247
248 def set_enemy_indices(self):
249 for i, enemy in enumerate(self.enemy_list):
250 enemy.index = i
251
252 def draw_battle(self, surface):
253 """Draw all elements of battle state"""
254 self.background.draw(surface)
255 self.enemy_group.draw(surface)
256 self.attack_animations.draw(surface)
257 self.sword.draw(surface)
258 surface.blit(self.player.image, self.player.rect)
259 surface.blit(self.info_box.image, self.info_box.rect)
260 surface.blit(self.select_box.image, self.select_box.rect)
261 surface.blit(self.arrow.image, self.arrow.rect)
262 self.player_health_box.draw(surface)
263 self.damage_points.draw(surface)
264
265
266 def player_damaged(self, damage):
267 self.game_data['player stats']['health']['current'] -= damage
268
269 def player_healed(self, heal):
270 health = self.game_data['player stats']['health']
271
272 health['current'] += heal
273 if health['current'] > health['maximum']:
274 health['current'] = health['maximum']
275
276 if self.state == c.DRINK_HEALING_POTION:
277 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
278 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
279 del self.game_data['player inventory']['Healing Potion']
280 elif self.state == c.CURE_SPELL:
281 self.game_data['player stats']['magic points']['current'] -= 25
282
283 def set_timer_to_current_time(self):
284 """Set the timer to the current time."""
285 self.timer = self.current_time
286
287