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