File size: 7,801 Bytes
a3e5f70 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | from settings import *
from random import uniform
from support import draw_bar
from timer import Timer
# overworld sprites
class Sprite(pygame.sprite.Sprite):
def __init__(self, pos, surf, groups, z = WORLD_LAYERS['main']):
super().__init__(groups)
self.image = surf
self.rect = self.image.get_frect(topleft = pos)
self.z = z
self.y_sort = self.rect.centery
self.hitbox = self.rect.copy()
class BorderSprite(Sprite):
def __init__(self, pos, surf, groups):
super().__init__(pos, surf, groups)
self.hitbox = self.rect.copy()
class TransitionSprite(Sprite):
def __init__(self, pos, size, target, groups):
surf = pygame.Surface(size)
super().__init__(pos, surf, groups)
self.target = target
class CollidableSprite(Sprite):
def __init__(self, pos, surf, groups):
super().__init__(pos, surf, groups)
self.hitbox = self.rect.inflate(0, -self.rect.height * 0.6)
class MonsterPatchSprite(Sprite):
def __init__(self, pos, surf, groups, biome, monsters, level):
self.biome = biome
super().__init__(pos, surf, groups, WORLD_LAYERS['main' if biome != 'sand' else 'bg'])
self.y_sort -= 40
self.biome = biome
self.monsters = monsters.split(',')
self.level = level
class AnimatedSprite(Sprite):
def __init__(self, pos, frames, groups, z = WORLD_LAYERS['main']):
self.frame_index, self.frames = 0, frames
super().__init__(pos, frames[self.frame_index], groups, z)
def animate(self, dt):
self.frame_index += ANIMATION_SPEED * dt
self.image = self.frames[int(self.frame_index % len(self.frames))]
def update(self, dt):
self.animate(dt)
# battle sprites
class MonsterSprite(pygame.sprite.Sprite):
def __init__(self, pos, frames, groups, monster, index, pos_index, entity, apply_attack, create_monster):
# data
self.index = index
self.pos_index = pos_index
self.entity = entity
self.monster = monster
self.frame_index, self.frames, self.state = 0, frames, 'idle'
self.animation_speed = ANIMATION_SPEED + uniform(-1, 1)
self.z = BATTLE_LAYERS['monster']
self.highlight = False
self.target_sprite = None
self.current_attack = None
self.apply_attack = apply_attack
self.create_monster = create_monster
# sprite setup
super().__init__(groups)
self.image = self.frames[self.state][self.frame_index]
self.rect = self.image.get_frect(center = pos)
# timers
self.timers = {
'remove highlight': Timer(300, func = lambda: self.set_highlight(False)),
'kill': Timer(600, func = self.destroy)
}
def animate(self, dt):
self.frame_index += ANIMATION_SPEED * dt
if self.state == 'attack' and self.frame_index >= len(self.frames['attack']):
self.apply_attack(self.target_sprite, self.current_attack, self.monster.get_base_damage(self.current_attack))
self.state = 'idle'
self.adjusted_frame_index = int(self.frame_index % len(self.frames[self.state]))
self.image = self.frames[self.state][self.adjusted_frame_index]
if self.highlight:
white_surf = pygame.mask.from_surface(self.image).to_surface()
white_surf.set_colorkey('black')
self.image = white_surf
def set_highlight(self, value):
self.highlight = value
if value:
self.timers['remove highlight'].activate()
def activate_attack(self, target_sprite, attack):
self.state = 'attack'
self.frame_index = 0
self.target_sprite = target_sprite
self.current_attack = attack
self.monster.reduce_energy(attack)
def delayed_kill(self, new_monster):
if not self.timers['kill'].active:
self.next_monster_data = new_monster
self.timers['kill'].activate()
def destroy(self):
if self.next_monster_data:
self.create_monster(*self.next_monster_data)
self.kill()
def update(self, dt):
for timer in self.timers.values():
timer.update()
self.animate(dt)
self.monster.update(dt)
class MonsterOutlineSprite(pygame.sprite.Sprite):
def __init__(self, monster_sprite, groups, frames):
super().__init__(groups)
self.z = BATTLE_LAYERS['outline']
self.monster_sprite = monster_sprite
self.frames = frames
self.image = self.frames[self.monster_sprite.state][self.monster_sprite.frame_index]
self.rect = self.image.get_frect(center = self.monster_sprite.rect.center)
def update(self, _):
self.image = self.frames[self.monster_sprite.state][self.monster_sprite.adjusted_frame_index]
if not self.monster_sprite.groups():
self.kill()
class MonsterNameSprite(pygame.sprite.Sprite):
def __init__(self, pos, monster_sprite, groups, font):
super().__init__(groups)
self.monster_sprite = monster_sprite
self.z = BATTLE_LAYERS['name']
text_surf = font.render(monster_sprite.monster.name, False, COLORS['black'])
padding = 10
self.image = pygame.Surface((text_surf.get_width() + 2 * padding, text_surf.get_height() + 2 * padding))
self.image.fill(COLORS['white'])
self.image.blit(text_surf, (padding, padding))
self.rect = self.image.get_frect(midtop = pos)
def update(self, _):
if not self.monster_sprite.groups():
self.kill()
class MonsterLevelSprite(pygame.sprite.Sprite):
def __init__(self, entity, pos, monster_sprite, groups, font):
super().__init__(groups)
self.monster_sprite = monster_sprite
self.font = font
self.z = BATTLE_LAYERS['name']
self.image = pygame.Surface((60,26))
self.rect = self.image.get_frect(topleft = pos) if entity == 'player' else self.image.get_frect(topright = pos)
self.xp_rect = pygame.FRect(0,self.rect.height - 2,self.rect.width,2)
def update(self, _):
self.image.fill(COLORS['white'])
text_surf = self.font.render(f'Lvl {self.monster_sprite.monster.level}', False, COLORS['black'])
text_rect = text_surf.get_frect(center = (self.rect.width / 2, self.rect.height / 2))
self.image.blit(text_surf, text_rect)
draw_bar(self.image, self.xp_rect, self.monster_sprite.monster.xp, self.monster_sprite.monster.level_up, COLORS['black'], COLORS['white'], 0)
if not self.monster_sprite.groups():
self.kill()
class MonsterStatsSprite(pygame.sprite.Sprite):
def __init__(self, pos, monster_sprite, size, groups, font):
super().__init__(groups)
self.monster_sprite = monster_sprite
self.image = pygame.Surface(size)
self.rect = self.image.get_frect(midbottom = pos)
self.font = font
self.z = BATTLE_LAYERS['overlay']
def update(self, _):
self.image.fill(COLORS['white'])
for index, (value, max_value) in enumerate(self.monster_sprite.monster.get_info()):
color = (COLORS['red'], COLORS['blue'], COLORS['gray'])[index]
if index < 2: # health and energy
text_surf = self.font.render(f'{int(value)}/{max_value}', False, COLORS['black'])
text_rect = text_surf.get_frect(topleft = (self.rect.width * 0.05,index * self.rect.height / 2))
bar_rect = pygame.FRect(text_rect.bottomleft + vector(0,-2), (self.rect.width * 0.9, 4))
self.image.blit(text_surf, text_rect)
draw_bar(self.image, bar_rect, value, max_value, color, COLORS['black'], 2)
else: # initiative
init_rect = pygame.FRect((0, self.rect.height - 2), (self.rect.width, 2))
draw_bar(self.image, init_rect, value, max_value, color, COLORS['white'], 0)
if not self.monster_sprite.groups():
self.kill()
class AttackSprite(AnimatedSprite):
def __init__(self, pos, frames, groups):
super().__init__(pos, frames, groups, BATTLE_LAYERS['overlay'])
self.rect.center = pos
def animate(self, dt):
self.frame_index += ANIMATION_SPEED * dt
if self.frame_index < len(self.frames):
self.image = self.frames[int(self.frame_index)]
else:
self.kill()
def update(self, dt):
self.animate(dt)
class TimedSprite(Sprite):
def __init__(self, pos, surf, groups, duration):
super().__init__(pos, surf, groups, z = BATTLE_LAYERS['overlay'])
self.rect.center = pos
self.death_timer = Timer(duration, autostart = True, func = self.kill)
def update(self, _):
self.death_timer.update() |