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 or self.state == c.SELECT_MAGIC:
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 self.allow_input = False
163
164 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
165 self.allow_input = True
166
167 def check_timed_events(self):
168 """
169 Check if amount of time has passed for timed events.
170 """
171 timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE,
172 c.ENEMY_HIT,
173 c.ENEMY_DEAD,
174 c.DRINK_HEALING_POTION]
175 long_delay = timed_states[1:]
176
177 if self.state in long_delay:
178 if (self.current_time - self.timer) > 600:
179 if self.state == c.ENEMY_HIT:
180 self.state = c.ENEMY_DEAD
181 elif self.state == c.ENEMY_DEAD:
182 if len(self.enemy_list):
183 self.state = c.ENEMY_ATTACK
184 else:
185 self.state = c.BATTLE_WON
186 elif self.state == c.DRINK_HEALING_POTION:
187 self.state = c.ENEMY_ATTACK
188 self.timer = self.current_time
189 self.notify(self.state)
190
191 elif self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
192 if (self.current_time - self.timer) > 600:
193 if self.enemy_index == (len(self.enemy_list) - 1):
194 self.state = c.SELECT_ACTION
195 else:
196 self.state = c.SWITCH_ENEMY
197 self.timer = self.current_time
198 self.notify(self.state)
199
200 def check_if_battle_won(self):
201 """
202 Check if state is SELECT_ACTION and there are no enemies left.
203 """
204 if self.state == c.SELECT_ACTION:
205 if len(self.enemy_group) == 0:
206 self.notify(c.BATTLE_WON)
207
208 def notify(self, event):
209 """
210 Notify observer of event.
211 """
212 for new_observer in self.observers:
213 new_observer.on_notify(event)
214
215 def end_battle(self):
216 """
217 End battle and flip back to previous state.
218 """
219 self.game_data['last state'] = self.name
220 self.done = True
221
222 def attack_enemy(self, enemy_damage):
223 enemy = self.player.attacked_enemy
224 enemy.health -= enemy_damage
225 self.set_enemy_indices()
226
227 if enemy:
228 if enemy.health <= 0:
229 enemy.kill()
230 self.enemy_list.pop(enemy.index)
231 self.arrow.remove_pos(self.player.attacked_enemy)
232 posx = enemy.rect.x - 32
233 posy = enemy.rect.y - 64
234 fire_sprite = attack.Fire(posx, posy)
235 self.attack_animations.add(fire_sprite)
236 self.enemy_index = 0
237 self.player.attacked_enemy = None
238
239 def set_enemy_indices(self):
240 for i, enemy in enumerate(self.enemy_list):
241 enemy.index = i
242
243 def draw_battle(self, surface):
244 """Draw all elements of battle state"""
245 self.background.draw(surface)
246 self.enemy_group.draw(surface)
247 self.attack_animations.draw(surface)
248 self.sword.draw(surface)
249 surface.blit(self.player.image, self.player.rect)
250 surface.blit(self.info_box.image, self.info_box.rect)
251 surface.blit(self.select_box.image, self.select_box.rect)
252 surface.blit(self.arrow.image, self.arrow.rect)
253 self.player_health_box.draw(surface)
254 self.damage_points.draw(surface)
255
256
257 def player_damaged(self, damage):
258 self.game_data['player stats']['health']['current'] -= damage
259
260 def player_healed(self, heal):
261 health = self.game_data['player stats']['health']
262
263 health['current'] += heal
264 if health['current'] > health['maximum']:
265 health['current'] = health['maximum']
266 self.game_data['player inventory']['Healing Potion']['quantity'] -= 1
267 if self.game_data['player inventory']['Healing Potion']['quantity'] == 0:
268 del self.game_data['player inventory']['Healing Potion']
269
270 def set_timer_to_current_time(self):
271 """Set the timer to the current time."""
272 self.timer = self.current_time
273
274