Skip to content

Instantly share code, notes, and snippets.

@ectrimble20
Created August 15, 2018 06:15
Show Gist options
  • Save ectrimble20/4258e5b70f40487cbc35c40443cd2b78 to your computer and use it in GitHub Desktop.
Save ectrimble20/4258e5b70f40487cbc35c40443cd2b78 to your computer and use it in GitHub Desktop.
Animation example for adding a Zelda like fall animation using a new state.
import pygame as pg
import pickle
from os import path
import traceback
from random import choice
import functions as fn
import settings as st
#import rooms as rm
vec = pg.math.Vector2
UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
exported_globals = globals()
def create(game, data):
d = data
g = game
# takes a dictionary of sprite properties
name = d['name'].capitalize()
#instantiate the sprite
exported_globals[name](g, (d['x'], d['y'] + st.GUI_HEIGHT),
(d['width'], d['height']))
class saveObject:
def __init__(self):
self.data = {}
self.filename = 'savefile.dat'
directory = path.dirname(__file__)
save_folder = path.join(directory, 'saves')
self.filename = path.join(save_folder, self.filename)
def save(self):
with open(self.filename, 'wb') as file:
pickle.dump(self, file)
def load(self):
try:
with open(self.filename, 'rb') as file:
self.data = pickle.load(file).data
#print(self.data)
except Exception:
traceback.print_exc()
return
class ImageLoader:
'''
A class that loads all images at the start of the game
'''
def __init__(self, game):
self.game = game
self.tileset_names = ['tileset_red_8x8.png']
def load(self):
self.player_img = {
'walk': fn.img_list_from_strip('knight_strip.png', 16, 16,
0, 10),
'attack': fn.img_list_from_strip('knight_attack.png', 16, 16,
0, 4)
}
self.tileset_dict = {key: fn.tileImageScale(key, 8, 8, scale=True)
for key in self.tileset_names}
# set the image for testing
self.tileset_image = fn.loadImage(self.tileset_names[0], 1)
self.solid_img ={
'block': fn.getSubimg(self.tileset_image, 16, 16, (16, 0))
}
self.room_img = fn.img_list_from_strip('minimap_strip_7x5.png',
7, 5, 0, 20, False)
self.room_image_dict = {
'empty': self.room_img[0],
'NSWE': self.room_img[1],
'N': self.room_img[3],
'E': self.room_img[4],
'S': self.room_img[5],
'W': self.room_img[6],
'NE': self.room_img[7],
'NS': self.room_img[8],
'NW': self.room_img[9],
'SE': self.room_img[10],
'WE': self.room_img[11],
'SW': self.room_img[12],
'NWE': self.room_img[13],
'NES': self.room_img[14],
'SWE': self.room_img[15],
'NWS': self.room_img[16]
}
self.enemy_img = {
'skeleton': fn.img_list_from_strip('skeleton_strip.png', 16, 16,
0, 2),
'slime': fn.img_list_from_strip('slime_strip.png', 16, 16, 0, 3),
'slime_small': fn.img_list_from_strip('slime_strip.png', 16, 16,
0, 3, size=(8, 8)),
'bat': fn.img_list_from_strip('bat_strip.png', 16, 16, 0, 2)
}
self.item_img = {
'sword': fn.loadImage('sword.png')
}
self.gui_img = {
'background': fn.loadImage('inventory_bg.png'),
'cursor': [fn.loadImage('cursor.png'),
pg.Surface((16, 16)).fill(st.TRANS)],
'health': fn.loadImage('health_string.png'),
'hearts': fn.img_list_from_strip('hearts_strip.png',8, 8,
0, 6, scale=False)
}
class Player(pg.sprite.Sprite):
def __init__(self, game, pos):
self.group = game.all_sprites
pg.sprite.Sprite.__init__(self)
self.layer = 10
self.group.add(self, layer=self.layer)
self.game = game
# images for animation
self.image_strip = self.game.imageLoader.player_img['walk']
self.walk_frames_l = [self.image_strip[6], self.image_strip[7]]
self.walk_frames_r = [self.image_strip[2], self.image_strip[3]]
self.walk_frames_u = [self.image_strip[4], self.image_strip[5]]
self.walk_frames_d = [self.image_strip[0], self.image_strip[1]]
self.idle_frames_l = [self.image_strip[6]]
self.idle_frames_r = [self.image_strip[2]]
self.idle_frames_u = [self.image_strip[9]]
self.idle_frames_d = [self.image_strip[8]]
self.fall_frames = [self.image_strip[6], self.image_strip[4], self.image_strip[2], self.image_strip[0]]
self.fall_time = 0
self.falling_time = 0
self.ticks_to_fall = 1500 # 1.5 seconds roughly
self.attack_strip = self.game.imageLoader.player_img['attack']
self.attack_frames_l = [self.attack_strip[3]]
self.attack_frames_r = [self.attack_strip[2]]
self.attack_frames_u = [self.attack_strip[1]]
self.attack_frames_d = [self.attack_strip[0]]
self.image = self.walk_frames_d[0]
self.rect = self.image.get_rect()
self.pos = vec(pos)
# spawning position in the room
self.spawn_pos = vec(pos)
self.rect.center = pos
self.hit_rect = st.PLAYER_HIT_RECT
self.hit_rect.center = self.rect.center
self.vel = vec(0, 0)
self.acc = vec(0, 0)
self.dir = vec(DOWN)
self.lastdir = vec(DOWN)
self.friction = st.PLAYER_FRICTION
self.state = 'IDLE'
self.max_hp = st.PLAYER_HP_START
self.hp = 3.0
self.itemA = None
self.itemB = None
self.sword = None
self.anim_update = 0
self.attack_update = 0
self.current_frame = 0
# testing a save function
self.saveGame = self.game.saveGame
def saveSelf(self):
self.saveGame.data = {**self.saveGame.data,
'pos': (self.pos.x, self.pos.y),
'state': self.state,
'hp': self.hp,
'itemA': self.itemA,
'itemB': self.itemB
}
self.saveGame.save()
def loadSelf(self):
try:
self.saveGame.load()
self.pos.x, self.pos.y = self.saveGame.data['pos']
self.state = self.saveGame.data['state']
self.hp = self.saveGame.data['hp']
self.itemA = self.saveGame.data['itemA']
self.itemB = self.saveGame.data['itemB']
except:
pass
def get_keys(self):
if self.state == 'IDLE' or self.state == 'WALKING':
keys = pg.key.get_pressed()
# set the acceleration vector based on key presses
move_x = ((keys[pg.K_RIGHT] or keys[pg.K_d]) -
(keys[pg.K_LEFT] or keys[pg.K_a]))
move_y = ((keys[pg.K_DOWN] or keys[pg.K_s]) -
(keys[pg.K_UP] or keys[pg.K_w]))
self.acc = vec(move_x, move_y) * st.PLAYER_ACC
# set image's direction based on key pressed
if move_x == -1:
self.dir = vec(LEFT)
self.lastdir = vec(LEFT)
if move_x == 1:
self.dir = vec(RIGHT)
self.lastdir = vec(RIGHT)
if move_y == -1:
self.dir = vec(UP)
self.lastdir = vec(UP)
if move_y == 1:
self.dir = vec(DOWN)
self.lastdir = vec(DOWN)
if self.acc.length() < 0.1:
# if velocity is less than the threshold, set state to idle
self.state = 'IDLE'
else:
# set the state to walking
self.state = 'WALKING'
if self.game.key_down == pg.K_SPACE:
self.state = 'ATTACK'
self.vel = vec(0, 0)
elif self.state == 'ATTACK':
self.attack()
self.attack_update += 1
if self.attack_update > 20:
self.attack_update = 0
self.state = 'IDLE'
elif self.state == 'HITSTUN' or self.state == 'FALL':
# can't receive input when stunned
pass
# FOR TESTING
if self.game.debug:
if self.game.key_down == pg.K_PAGEUP:
self.hp += 0.25
elif self.game.key_down == pg.K_PAGEDOWN:
self.hp -= 0.25
def update(self, others):
if self.state == 'FALL':
if self.fall_time == 0:
self.fall_time = pg.time.get_ticks()
self.fall_time += self.ticks_to_fall # add N seconds for the fall time
if self.falling_time > self.fall_time: # fall until time passes N amount
self.falling_time = 0
self.fall_time = 0
self.state = 'IDLE'
self.pos = vec(self.spawn_pos)
self.stun(3)
else:
self.falling_time = pg.time.get_ticks() # set fall time to current time
# get player input
self.get_keys()
# add acceleration to velocity
self.vel += self.acc
# apply friction
self.vel *= (1 - self.friction)
# limit velocity
if self.vel.length_squared() > st.PLAYER_MAXSPEED ** 2:
self.vel.scale_to_length(st.PLAYER_MAXSPEED)
elif self.vel.length_squared() < 0.01:
self.vel *= 0
# add velocity to position
self.pos += self.vel
if self.state != 'HITSTUN':
self.acc *= 0
self.rect = self.image.get_rect()
self.rect.center = self.pos
# collision detection
self.hit_rect.centerx = self.pos.x
fn.collide_with_walls(self, self.game.walls, 'x')
self.hit_rect.centery = self.pos.y
fn.collide_with_walls(self, self.game.walls, 'y')
# position the hitrect at the bottom of the image
self.rect.midbottom = self.hit_rect.midbottom
# restrain hp between 0 and max
self.hp = max(0, min(self.hp, self.max_hp))
# player animations
self.animate()
def animate(self):
now = pg.time.get_ticks()
if self.state == 'WALKING':
if now - self.anim_update > 200:
self.anim_update = now
self.current_frame = (self.current_frame + 1) % len(
self.walk_frames_l)
if self.dir == RIGHT:
self.image = self.walk_frames_r[self.current_frame]
elif self.dir == LEFT:
self.image = self.walk_frames_l[self.current_frame]
if self.dir == DOWN:
self.image = self.walk_frames_d[self.current_frame]
elif self.dir == UP:
self.image = self.walk_frames_u[self.current_frame]
elif self.state == 'FALL':
if now - self.anim_update > 100:
self.anim_update = now
self.current_frame = (self.current_frame + 1) % len(
self.fall_frames)
self.image = self.fall_frames[self.current_frame]
elif self.state == 'IDLE':
if self.lastdir == RIGHT:
self.image = self.idle_frames_r[0]
elif self.lastdir == LEFT:
self.image = self.idle_frames_l[0]
if self.lastdir == DOWN:
self.image = self.idle_frames_d[0]
elif self.lastdir == UP:
self.image = self.idle_frames_u[0]
elif self.state == 'ATTACK':
if self.lastdir == RIGHT:
self.image = self.attack_frames_r[0]
elif self.lastdir == LEFT:
self.image = self.attack_frames_l[0]
if self.lastdir == DOWN:
self.image = self.attack_frames_d[0]
elif self.lastdir == UP:
self.image = self.attack_frames_u[0]
elif self.state == 'HITSTUN':
# flicker to indicate damage
try:
alpha = next(self.damage_alpha)
self.image = self.lastimage.copy()
self.image.fill((255, 255, 255, alpha),
special_flags=pg.BLEND_RGBA_MULT)
except:
self.state = 'IDLE'
def attack(self):
if not self.game.all_sprites.has(self.sword):
self.sword = Sword(self.game, self)
def stun(self, time):
self.vel *= 0
self.acc *= 0
self.state = 'HITSTUN'
self.lastimage = self.image.copy()
self.damage_alpha = iter(st.DAMAGE_ALPHA * time)
def knockback(self, other, time, intensity):
if self.state != 'HITSTUN':
self.vel = vec(0, 0)
# calculate vector from other to self
knockdir = self.pos - other.pos
knockdir = knockdir.normalize()
self.acc = knockdir * st.GLOBAL_SCALE * intensity
self.state = 'HITSTUN'
self.lastimage = self.image.copy()
self.damage_alpha = iter(st.DAMAGE_ALPHA * time)
class Solid(pg.sprite.Sprite):
'''
Container Class for all solid objects
'''
def __init__(self):
self.groups = self.game.walls, self.game.all_sprites
self.layer = 1
pg.sprite.Sprite.__init__(self)
for g in self.groups:
g.add(self, layer=self.layer)
self.rect = pg.Rect(self.pos, self.size)
self.hit_rect = self.rect
def update(self):
# not used right now
pass
class Wall(Solid):
'''
An invisible wall object with variable size
'''
def __init__(self, game, pos, size):
self.game = game
self.pos = vec(pos)
self.size = size
self.image = pg.Surface(size, pg.SRCALPHA)
self.image.fill((0, 0, 0, 0))
super().__init__()
class Block(Solid):
'''
A solid block with an image, always same size
'''
def __init__(self, game, pos, size):
self.game = game
self.pos = vec(pos)
self.size = size
self.image = self.game.imageLoader.solid_img['block']
super().__init__()
class Hole(pg.sprite.Sprite):
'''
A hole that the player can fall into (and spawn at the entrance)
'''
def __init__(self, game, pos, size):
self.game = game
self.group = self.game.all_sprites
self.layer = 1
pg.sprite.Sprite.__init__(self)
self.group.add(self, layer=self.layer)
self.pos = vec(pos)
self.size = size
self.image = pg.Surface(size, pg.SRCALPHA)
self.image.fill((0, 0, 0, 0))
self.rect = self.image.get_rect()
self.rect.topleft = self.pos
self.hit_rect = pg.Rect((0, 0), (int(st.TILESIZE * 0.6),
int(st.TILESIZE * 0.6)))
self.hit_rect.center = self.rect.center
def update(self):
# detect collision
player = self.game.player
if player.state != 'FALL':
if fn.collide_hit_rect(player, self):
# Attract the player to the center of the hole
desired = self.rect.center - player.pos
mag = desired.length()
force = desired.normalize() * st.GLOBAL_SCALE * 10 / mag
player.pos += force
if desired.length_squared() < 50:
player.state = 'FALL'
# set the player back to the entrance point
# player.pos = vec(player.spawn_pos)
# player.stun(3)
# --------------- Inventory & Items -------------------------------------------
class Inventory(pg.sprite.Sprite):
def __init__(self, game):
self.groups = game.gui
pg.sprite.Sprite.__init__(self, self.groups)
self.game = game
# if in menu then True, otherwise False
self.menu = False
self.size = (st.WIDTH, st.HEIGHT)
self.start_pos = vec(0, (0 - st.HEIGHT + st.GUI_HEIGHT))
self.pos = vec(self.start_pos)
self.image = pg.Surface(self.size)
self.image.fill(st.BLACK)
self.map_img = None
# inventory background
self.gui_img = self.game.imageLoader.gui_img['background']
self.cursor_images = self.game.imageLoader.gui_img['cursor']
self.cursor_pos = vec(24 * st.GLOBAL_SCALE, 40 * st.GLOBAL_SCALE)
# "health" string
self.health_string = self.game.imageLoader.gui_img['health']
# images for the player health
self.heart_images = self.game.imageLoader.gui_img['hearts']
for i in range(len(self.heart_images)):
self.heart_images[i] = pg.transform.scale(self.heart_images[i],
(8 * st.GLOBAL_SCALE, 8 * st.GLOBAL_SCALE))
self.inv_index = [0, 0]
def update(self):
if self.game.key_down == pg.K_ESCAPE:
self.menu = not self.menu
if self.menu:
self.game.state = 'MENU'
# sliding down animation
if self.pos != (0, 0):
self.pos.y += st.SCROLLSPEED_MENU
self.pos.y = min(0, self.pos.y)
self.move_cursor()
else:
# sliding up animation
if self.pos != self.start_pos:
self.pos.y -= st.SCROLLSPEED_MENU
self.pos.y = min(0, self.pos.y)
else:
self.game.state = 'GAME'
def draw(self):
self.image.fill(st.BLACK)
# draw player health
player = self.game.player
for i in range(int(player.max_hp)):
# calculate position
if i < st.PLAYER_HP_MAX // 2:
pos = (6 * st.GLOBAL_SCALE + 10 * i * st.GLOBAL_SCALE,
(st.HEIGHT - 34 * st.GLOBAL_SCALE))
else:
pos = (6 * st.GLOBAL_SCALE + 10 * (i - 7) * st.GLOBAL_SCALE,
(st.HEIGHT - 24 * st.GLOBAL_SCALE))
# draw hearts:
if i < int(player.hp):
img = self.heart_images[1]
elif i == int(player.hp):
if player.hp % 1 == 0.25:
img = self.heart_images[4]
elif player.hp % 1 == 0.5:
img = self.heart_images[3]
elif player.hp % 1 == 0.75:
img = self.heart_images[2]
else:
img = self.heart_images[5]
else:
img = self.heart_images[5]
self.image.blit(img, pos)
self.image.blit(self.health_string, (25 * st.GLOBAL_SCALE,
st.HEIGHT - 42 * st.GLOBAL_SCALE))
# draw the mini map
map_pos = (192 * st.GLOBAL_SCALE, st.HEIGHT - 44 * st.GLOBAL_SCALE)
self.image.blit(self.map_img, map_pos)
# draw the inventory background
self.image.blit(self.gui_img, (0, 0))
self.draw_cursor()
self.game.screen.blit(self.image, self.pos)
def move_cursor(self):
key = self.game.key_down
# set the movement vector based on key presses
move_x = (key == pg.K_RIGHT or key == pg.K_d) - (key == pg.K_LEFT or
key == pg.K_a)
move_y = (key == pg.K_DOWN or key == pg.K_s) - (key == pg.K_UP or
key == pg.K_w)
# move the cursor
self.cursor_pos += vec(move_x, move_y) * 24 * st.GLOBAL_SCALE
self.cursor_pos.x = fn.clamp(self.cursor_pos.x, 24 * st.GLOBAL_SCALE,
120 * st.GLOBAL_SCALE)
self.cursor_pos.y = fn.clamp(self.cursor_pos.y, 40 * st.GLOBAL_SCALE,
136 * st.GLOBAL_SCALE)
def draw_cursor(self):
self.image.blit(self.cursor_images[0], self.cursor_pos)
class Sword(pg.sprite.Sprite):
def __init__(self, game, player):
self.group = game.all_sprites
self.layer = player.layer
pg.sprite.Sprite.__init__(self)
self.group.add(self, layer=self.layer)
self.player = player
self.game = game
self.image = self.game.imageLoader.item_img['sword']
self.pos = vec(0, 0)
self.rot = 0
self.damage = 1
self.dir = self.player.lastdir
# rotate image based on player direction and set position
if self.dir == UP:
self.rot = 0
self.pos = vec(self.player.pos.x - 4 * st.GLOBAL_SCALE,
self.player.pos.y - 24 * st.GLOBAL_SCALE)
elif self.dir == DOWN:
self.rot = 180
self.pos = vec(self.player.pos.x, self.player.pos.y + 4
* st.GLOBAL_SCALE)
elif self.dir == RIGHT:
self.rot = 270
self.pos = vec(self.player.pos.x + 7 * st.GLOBAL_SCALE,
self.player.pos.y - 4 * st.GLOBAL_SCALE)
elif self.dir == LEFT:
self.rot = 90
self.pos = vec(self.player.pos.x - 20 * st.GLOBAL_SCALE,
self.player.pos.y - 4 * st.GLOBAL_SCALE)
self.image = pg.transform.rotate(self.image, self.rot)
self.rect = self.image.get_rect()
self.rect.topleft = self.pos
self.hit_rect = self.rect
self.hit_rect.center = self.rect.center
def update(self):
if not self.player.state == 'ATTACK':
self.game.all_sprites.remove(self)
for enemy in pg.sprite.spritecollide(self, self.game.enemies, False):
if enemy.state != 'HITSTUN':
enemy.hp -= self.damage
enemy.knockback(self.player, 1, 0.1)
def draw(self):
self.game.screen.blit(self.image, self.pos)
# ----------------------- ENEMIES ---------------------------------------------
class Enemy(pg.sprite.Sprite):
'''
Container class for all enemies
'''
def __init__(self):
self.groups = self.game.all_sprites, self.game.enemies
self.layer = self.game.player.layer + 1
pg.sprite.Sprite.__init__(self)
for g in self.groups:
g.add(self, layer=self.layer)
self.image = self.walk_frames[0]
self.rect = self.image.get_rect()
self.rect.center = self.pos
self.vel = vec(0, 0)
self.dir = vec(DOWN)
self.lastdir = vec(DOWN)
self.moveTo = None
self.acc = vec(0, 0)
self.friction = 0.1
self.state = 'IDLE'
# default values (change in individual init after super().__init__())
self.maxSpeed = 0.5 * st.GLOBAL_SCALE
self.anim_update = 0
self.walk_update = 0
self.current_frame = 0
self.anim_speed = 300
# testing a save function
self.saveGame = self.game.saveGame
def move(self):
if self.state == 'IDLE' or self.state == 'WALKING':
# add acceleration to velocity
if self.moveTo == LEFT:
self.acc.x = -1
self.dir = vec(LEFT)
self.lastdir = vec(LEFT)
if self.moveTo == RIGHT:
self.acc.x = 1
self.dir = vec(RIGHT)
self.lastdir = vec(RIGHT)
if self.moveTo == UP:
self.acc.y = -1
self.dir = vec(UP)
self.lastdir = vec(UP)
if self.moveTo == DOWN:
self.acc.y = 1
self.dir = vec(DOWN)
self.lastdir = vec(DOWN)
self.acc *= st.GLOBAL_SCALE
if self.acc.length() > 0.2:
self.state = 'WALKING'
else:
self.state = 'IDLE'
elif self.state == 'HITSTUN':
# can't receive input when stunned
pass
def update(self):
# change the drawing layer in relation to the player
if self.hit_rect.top > self.game.player.hit_rect.top:
for g in self.groups:
g.change_layer(self, self.game.player.layer + 1)
else:
for g in self.groups:
g.change_layer(self, self.game.player.layer - 1)
# change the moving direction after a certain time
now = pg.time.get_ticks()
if now - self.walk_update > 2000:
self.walk_update = now
self.moveTo = choice([LEFT, RIGHT, DOWN, UP])
self.move()
# add acceleration to velocity
self.vel += self.acc
if self.state != 'HITSTUN':
self.acc *= 0
# apply friction
self.vel *= (1 - self.friction)
# cap speed at maximum
if self.vel.length_squared() > self.maxSpeed ** 2:
self.vel.scale_to_length(self.maxSpeed)
self.pos += self.vel
# stop from sliding infinitely
if self.vel.length() < 0.1:
self.vel = vec(0, 0)
# update the positions
self.rect = self.image.get_rect()
self.rect.center = self.pos
# collision
self.hit_rect.centerx = self.pos.x
fn.collide_with_walls(self, self.game.walls, 'x')
self.hit_rect.centery = self.pos.y
fn.collide_with_walls(self, self.game.walls, 'y')
# restrain position to stay in the room
self.pos.x = fn.clamp(self.pos.x, st.TILESIZE * 2,
st.WIDTH - st.TILESIZE * 2)
self.pos.y = fn.clamp(self.pos.y, st.GUI_HEIGHT + st.TILESIZE * 2,
st.HEIGHT - st.TILESIZE * 2)
# position the hitrect at the bottom of the image
self.rect.midbottom = self.hit_rect.midbottom
self.collide_with_player()
if self.hp <= 0:
self.destroy()
self.animate()
def animate(self):
now = pg.time.get_ticks()
if self.state == 'WALKING':
if now - self.anim_update > self.anim_speed:
self.anim_update = now
self.current_frame = (self.current_frame + 1) % len(
self.walk_frames)
self.image = self.walk_frames[self.current_frame]
elif self.state == 'HITSTUN':
# flicker to indicate damage
try:
alpha = next(self.damage_alpha)
self.image = self.lastimage.copy()
self.image.fill((255, 255, 255, alpha),
special_flags=pg.BLEND_RGBA_MULT)
except:
self.state = 'IDLE'
def collide_with_player(self):
# detect collision
player = self.game.player
if fn.collide_hit_rect(player, self) and player.state != 'HITSTUN':
player.knockback(self, self.kb_time, self.kb_intensity)
player.hp -= self.damage
def knockback(self, other, time, intensity):
if self.state != 'HITSTUN':
self.vel = vec(0, 0)
# calculate vector from other to self
knockdir = self.pos - other.pos
knockdir = knockdir.normalize()
self.acc = knockdir * st.GLOBAL_SCALE * intensity
self.state = 'HITSTUN'
self.lastimage = self.image.copy()
self.damage_alpha = iter(st.DAMAGE_ALPHA * time)
def destroy(self):
self.kill()
class Skeleton(Enemy):
def __init__(self, game, pos, *args):
self.game = game
self.pos = vec(pos)
self.walk_frames = self.game.imageLoader.enemy_img['skeleton']
super().__init__()
self.hit_rect = pg.Rect(0, 0, int(st.TILESIZE * 0.8),
int(st.TILESIZE * 0.6))
self.damage = 0.5
self.hp = 3
# knockback stats
self.kb_time = 1
self.kb_intensity = 1
class Slime(Enemy):
def __init__(self, game, pos, *args):
self.game = game
self.pos = vec(pos)
self.walk_frames = self.game.imageLoader.enemy_img['slime']
super().__init__()
self.hit_rect = pg.Rect(0, 0, int(st.TILESIZE * 0.8),
int(st.TILESIZE * 0.6))
self.damage = 0.5
self.hp = 3
# knockback stats
self.kb_time = 1
self.kb_intensity = 1
def destroy(self):
# create two little slimes
for i in range(-45, 90, 90):
# TO DO: calculate the rotation relative to the player
rot = (self.pos - self.game.player.pos).rotate(i)
rot = rot.normalize() * st.GLOBAL_SCALE
s = SlimeSmall(self.game, self.pos + rot)
s.knockback(self, 2, 0.05)
super().destroy()
class SlimeSmall(Enemy):
def __init__(self, game, pos, *args):
self.game = game
self.pos = vec(pos)
self.walk_frames = self.game.imageLoader.enemy_img['slime_small']
super().__init__()
self.hit_rect = pg.Rect(0, 0, int(st.TILESIZE * 0.4),
int(st.TILESIZE * 0.3))
self.maxspeed = 10 * st.GLOBAL_SCALE
self.friction = 0
self.damage = 0.25
self.hp = 2
# knockback stats
self.kb_time = 1
self.kb_intensity = 1
def destroy(self):
super().destroy()
class Bat(Enemy):
def __init__(self, game, pos, *args):
self.game = game
self.pos = vec(pos)
self.walk_frames = self.game.imageLoader.enemy_img['bat']
super().__init__()
self.hit_rect = pg.Rect(0, 0, int(st.TILESIZE * 0.8),
int(st.TILESIZE * 0.6))
self.anim_speed = 150
self.damage = 0.5
self.hp = 3
# knockback stats
self.kb_time = 1
self.kb_intensity = 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment