data/states/battle.py (view raw)
1"""This is the state that handles battles against
2monsters"""
3
4import random
5import pygame as pg
6from .. import tools, battlegui, observer
7from .. components import person, attack, attackitems
8from .. import constants as c
9
10
11
12class Battle(tools._State):
13 def __init__(self):
14 super(Battle, self).__init__()
15
16 def startup(self, current_time, game_data):
17 """Initialize state attributes"""
18 self.current_time = current_time
19 self.timer = current_time
20 self.allow_input = False
21 self.allow_info_box_change = False
22 self.game_data = game_data
23 self.background = self.make_background()
24 self.enemy_group, self.enemy_pos_list, self.enemy_list = self.make_enemies()
25 self.enemy_index = 0
26 self.player = self.make_player()
27 self.attack_animations = pg.sprite.Group()
28 self.info_box = battlegui.InfoBox(game_data)
29 self.arrow = battlegui.SelectArrow(self.enemy_pos_list,
30 self.info_box)
31 self.select_box = battlegui.SelectBox()
32 self.player_health = battlegui.PlayerHealth(self.select_box.rect,
33 self.game_data)
34 self.sword = attackitems.Sword(self.player)
35 self.attacked_enemy = None
36 self.attacking_enemy = None
37 self.state = c.SELECT_ACTION
38 self.select_action_state_dict = self.make_selection_state_dict()
39 self.name = 'battle'
40 self.next = game_data['last state']
41 self.observers = [observer.Battle(self)]
42 self.player.observers.extend(self.observers)
43
44 def make_background(self):
45 """Make the blue/black background"""
46 background = pg.sprite.Sprite()
47 surface = pg.Surface(c.SCREEN_SIZE).convert()
48 surface.fill(c.BLACK_BLUE)
49 background.image = surface
50 background.rect = background.image.get_rect()
51 background_group = pg.sprite.Group(background)
52
53 return background_group
54
55 def make_enemies(self):
56 """Make the enemies for the battle. Return sprite group"""
57 pos_list = []
58
59 for column in range(3):
60 for row in range(3):
61 x = (column * 100) + 100
62 y = (row * 100) + 100
63 pos_list.append([x,y])
64
65 enemy_group = pg.sprite.Group()
66
67 for enemy in range(random.randint(1, 6)):
68 enemy_group.add(person.Person('devil', 0, 0,
69 'down', 'battle resting'))
70
71 for i, enemy in enumerate(enemy_group):
72 enemy.rect.topleft = pos_list[i]
73 enemy.image = pg.transform.scale2x(enemy.image)
74 enemy.index = i
75
76 enemy_list = [enemy for enemy in enemy_group]
77
78 return enemy_group, pos_list[0:len(enemy_group)], enemy_list
79
80 def make_player(self):
81 """Make the sprite for the player's character"""
82 player = person.Player('left', 630, 220, 'battle resting', 1)
83 player.image = pg.transform.scale2x(player.image)
84
85 return player
86
87 def make_selection_state_dict(self):
88 """
89 Make a dictionary of states with arrow coordinates as keys.
90 """
91 pos_list = self.arrow.make_select_action_pos_list()
92 state_list = [c.SELECT_ENEMY, c.SELECT_ITEM, c.SELECT_MAGIC, c.RUN_AWAY]
93 return dict(zip(pos_list, state_list))
94
95 def update(self, surface, keys, current_time):
96 """Update the battle state"""
97 self.current_time = current_time
98 self.check_input(keys)
99 self.check_for_timed_events()
100 self.check_if_battle_won()
101 self.enemy_group.update(current_time)
102 self.player.update(keys, current_time)
103 self.attack_animations.update()
104 self.info_box.update()
105 self.arrow.update(keys)
106
107 self.draw_battle(surface)
108
109 def check_input(self, keys):
110 """
111 Check user input to navigate GUI.
112 """
113 if self.allow_input:
114 if keys[pg.K_RETURN]:
115 self.notify(c.END_BATTLE)
116
117 elif keys[pg.K_SPACE]:
118 if self.state == c.SELECT_ACTION:
119 self.state = self.select_action_state_dict[
120 self.arrow.rect.topleft]
121 self.notify(self.state)
122
123 elif self.state == c.SELECT_ENEMY:
124 self.state = c.PLAYER_ATTACK
125 self.notify(self.state)
126
127 elif self.state == c.PLAYER_ATTACK:
128 self.state = c.ENEMY_DEAD
129 self.info_box.state = c.ENEMY_DEAD
130
131 elif self.state == c.SELECT_ITEM or self.state == c.SELECT_MAGIC:
132 if self.arrow.index == (len(self.arrow.pos_list) - 1):
133 self.state = c.SELECT_ACTION
134 self.notify(self.state)
135
136 self.allow_input = False
137
138 if keys[pg.K_RETURN] == False and keys[pg.K_SPACE] == False:
139 self.allow_input = True
140
141 def check_for_timed_events(self):
142 """
143 Check if amount of time has passed for timed events.
144 """
145 timed_states = [c.DISPLAY_ENEMY_ATTACK_DAMAGE, c.ENEMY_HIT, c.ENEMY_DEAD]
146
147 if self.state in timed_states:
148 if (self.current_time - self.timer) > 1500:
149 if self.state == c.DISPLAY_ENEMY_ATTACK_DAMAGE:
150 if self.enemy_index == (len(self.enemy_list) - 1):
151 self.state = c.SELECT_ACTION
152 else:
153 self.state = c.SWITCH_ENEMY
154 elif self.state == c.ENEMY_HIT:
155 self.state = c.ENEMY_DEAD
156 elif self.state == c.ENEMY_DEAD:
157 if len(self.enemy_list):
158 self.state = c.ENEMY_ATTACK
159 else:
160 self.state = c.BATTLE_WON
161
162 self.notify(self.state)
163
164
165 def check_if_battle_won(self):
166 """
167 Check if state is SELECT_ACTION and there are no enemies left.
168 """
169 if self.state == c.SELECT_ACTION:
170 if len(self.enemy_group) == 0:
171 self.notify(c.BATTLE_WON)
172
173 def notify(self, event):
174 """
175 Notify observer of event.
176 """
177 for observer in self.observers:
178 observer.on_notify(event)
179
180 def end_battle(self):
181 """
182 End battle and flip back to previous state.
183 """
184 self.game_data['last state'] = self.name
185 self.done = True
186
187 def attack_enemy(self):
188 enemy = self.player.attacked_enemy
189 self.set_enemy_indices()
190
191 if enemy:
192 enemy.kill()
193 self.enemy_list.pop(enemy.index)
194 self.enemy_index = 0
195 posx = enemy.rect.x - 32
196 posy = enemy.rect.y - 64
197 fire_sprite = attack.Fire(posx, posy)
198 self.attack_animations.add(fire_sprite)
199 self.player.attacked_enemy = None
200
201 def set_enemy_indices(self):
202 for i, enemy in enumerate(self.enemy_list):
203 enemy.index = i
204
205
206 def draw_battle(self, surface):
207 """Draw all elements of battle state"""
208 self.background.draw(surface)
209 self.enemy_group.draw(surface)
210 self.attack_animations.draw(surface)
211 surface.blit(self.player.image, self.player.rect)
212 surface.blit(self.info_box.image, self.info_box.rect)
213 surface.blit(self.select_box.image, self.select_box.rect)
214 surface.blit(self.arrow.image, self.arrow.rect)
215 self.player_health.draw(surface)
216 self.sword.draw(surface)
217
218 def player_damaged(self, damage):
219 self.game_data['player stats']['Health']['current'] -= damage
220
221 def set_timer_to_current_time(self):
222 """Set the timer to the current time."""
223 self.timer = self.current_time
224
225