class Ninjat(Ninja): def __init__(self, cam, clan="fire"): if not GLOBALS["ninjats"].has_key(clan): clan = "fire" Ninja.__init__(self, cam, GLOBALS["ninjats"][clan]) self.name = "ninjat" self.ai = 1 self.speed = 2 self.set_vel((-1, 0)) self.sit_timer = Timer(10) self.shuriken_timer.set_interval(70) self.reaction_timer = Timer(10) def update(self): Ninja.update(self) def ai_update(self, parent): if self.off_screen(): return 0 elif self.status == 8: #not visible or dying px, py = self.pos self.set_pos((px, py + 4)) return 0 blocks = parent.blocks vx, vy = self.vel #can you see nanji? nanji = parent.player if not nanji.status == 7 and not self.shuriken_timer.active: nanji_rect = nanji.colrect own_rect = self.colrect in_height = own_rect.top <= nanji_rect.centery <= own_rect.bottom if self.look < 0: in_look = own_rect.left > nanji_rect.left else: in_look = own_rect.right < nanji_rect.right if in_height and in_look and self.reaction_timer.update(): if not self.shuriken_timer.active: self.throw() self.update() self.reaction_timer.activate() return 1 #loockityerfeets, little kitteh - try not to fall down >.< if self.look > 0: x = self.colrect.right + vx x2 = self.colrect.left else: x = self.colrect.left - vx x2 = self.colrect.right front_paws = pygame.Rect([x, self.colrect.bottom + 10, 2, 2]) back_paws = pygame.Rect([x2, self.colrect.bottom + 10, 2, 2]) if front_paws.collidelist(blocks) < 0: #no graunds in front :/ if back_paws.collidelist( blocks) > -1: #but grawnds in bak. go bak! :) self.turn() self.mod_vel(vx=self.look) else: self.mod_vel(vx=0) #fall down. #turn around if bumped against block col_x, col_y = self.check_mapcollisions(blocks) if col_x: self.turn() self.mod_vel(vx=self.look) self.update() if self.status == 0 and self.sit_timer.update(): self.set_vel((self.look, 0)) return 0
class Level(State): def __init__(self, lvl_map="tutorial.map", testing=False, prev=None): State.__init__(self) self.fps = 60 self.prev = prev tsize = GLOBALS["tile_size"] self.trigger = None self.exit_timer = Timer(50) self.exit_timer.deactivate() pygame.mixer.music.set_volume(1) GLOBALS["jb"].play("bgm.xm", -1) player_data["health"] = 9 #always full health on map start #player_data["lives"] = #for gameover testing #scrolling background - maybe later XD #bg_back = GLOBALS["backgrounds"]["fuji.png"] #bg_front = GLOBALS["backgrounds"]["trees.png"] #self.scroll_bg = Background(bg_back, bg_front) #non-scrolling background self.bg = GLOBALS["backgrounds"]["fuji.png"] #map self.map_filename = lvl_map self.map = Map(lvl_map) self.lvl_map = self.map["tilemap"] x, y = self.map["max"] self.lvl_img, self.blocks = self.map.draw(editing=False) self.lvl_layer = pygame.Surface(self.lvl_img.get_size()) #other spritegroups self.bullets = pygame.sprite.Group() #player self.player = Nanji(None) self.player.set_pos((self.map["start"][0]*tsize, self.map["start"][1]*tsize)) self.sprites.add(self.player) #camera self.camera = Camera(self.player) screen_x, screen_y = GLOBALS["screen_size"] mx = self.map["max"][0] * tsize - screen_x my = self.map["max"][1] * tsize - screen_y + 4*tsize self.camera.set_max(mx,my) self.player.camera = self.camera self.camera.update() #add enemies self.enemies = pygame.sprite.Group() clan = self.map["options"]["clan"] for e in self.map.enemies: ninjat = Ninjat(self.camera, clan) ninjat.set_pos((e[0]*tsize, e[1]*tsize-32))#FIXME:enemies are spawned too low..? self.enemies.add(ninjat) self.sprites.add(ninjat) #ui layer self.ui_layer = StaticWidget((GLOBALS["screen_size"][0], 30)) self.ui_layer.fill((255,0,255)) self.ui_layer.set_colorkey((255,0,255)) self.ui_layer.set_pos((0,0)) self.heart_img = GLOBALS["ui_stuff"]["heart"] self.heart_empty_img = GLOBALS["ui_stuff"]["heart_empty"] self.life_img = GLOBALS["ui_stuff"]["life"] self.fontholder = TextLine("", fontsize=16) self.update_ui() #keyboard events self.key_pressed = { "up": 0, "down": 0, "left": 0, "right": 0, "jump":0, "sprint":0, "throw": 0, "shuriken": 0, } self.add_kbevent(KEYDOWN, K_LEFT, self.set_keypress, k="left", v=1) #move self.add_kbevent(KEYDOWN, K_RIGHT, self.set_keypress, k="right", v=1) self.add_kbevent(KEYDOWN, K_UP, self.set_keypress, k="up", v=1) self.add_kbevent(KEYDOWN, K_DOWN, self.set_keypress, k="down", v=1) self.add_kbevent(KEYDOWN, K_SPACE, self.set_keypress, k="jump", v=1) #jump self.add_kbevent(KEYDOWN, K_LSHIFT, self.set_keypress, k="sprint", v=1) #sprint self.add_kbevent(KEYDOWN, K_RSHIFT, self.set_keypress, k="sprint", v=1) self.add_kbevent(KEYDOWN, K_RCTRL, self.set_keypress, k="throw", v=1) #throw self.add_kbevent(KEYDOWN, K_LCTRL, self.set_keypress, k="throw", v=1) self.add_kbevent(KEYUP, K_LEFT, self.set_keypress, k="left", v=0) self.add_kbevent(KEYUP, K_RIGHT, self.set_keypress, k="right", v=0) self.add_kbevent(KEYUP, K_UP, self.set_keypress, k="up", v=0) self.add_kbevent(KEYUP, K_DOWN, self.set_keypress, k="down", v=0) self.add_kbevent(KEYUP, K_SPACE, self.set_keypress, k="jump", v=0) self.add_kbevent(KEYUP, K_LSHIFT, self.set_keypress, k="sprint", v=0) self.add_kbevent(KEYUP, K_RSHIFT, self.set_keypress, k="sprint", v=0) self.add_kbevent(KEYUP, K_RCTRL, self.set_keypress, k="throw", v=0) self.add_kbevent(KEYUP, K_LCTRL, self.set_keypress, k="throw", v=0) if testing: self.add_kbevent(KEYUP, K_ESCAPE, self.quit) def main_start(self): pygame.mouse.set_visible(0) State.main_start(self) def reset_keys(self): self.key_pressed = { "up": 0, "down": 0, "left": 0, "right": 0, "jump":0, "sprint":0, "throw": 0, "shuriken": 0, } def update_other(self): if self.player.exiting: self.show_exit_anim() return kp = self.key_pressed vx = 0 if kp["left"] and not kp["right"]: vx = -1 if kp["right"] and not kp["left"]: vx = 1 self.player.mod_vel(vx=vx) if kp["jump"]: self.player.jump() elif self.player.status==4: self.player.unjump() if kp["sprint"]: self.player.sprint() #self.scroll_bg.front_speed = 8 elif self.player.status==2: self.player.unsprint() #self.scroll_bg.front_speed = 4 if kp["down"]: self.player.duck() elif self.player.status==7: self.player.unduck() if kp["throw"]: kp["throw"] = 0 if self.player.throw(): self.bullets.add(Shuriken(self.player, self.camera)) def set_keypress(self, k, v): self.key_pressed[k] = v def show_exit_anim(self): self.fps = 30 self.reset_keys() if self.player.exiting == -1: self.player.set_vel((0,0)) self.player.set_image(GLOBALS["nanji"]["idle"][0]) if not self.exit_timer.active: self.exit_timer.activate() elif self.player.vel[1]: self.player.status = 5 else: self.player.status = 1 self.player.mod_vel(vx=1) if self.player.pos[0] > self.map["exit"][0]*tsize+10*tsize or \ self.exit_timer.update(): #TODO: play some funky tune / do SOMETHING on level exit if self.prev: self.prev.success = True self.quit() def show_dialog(self, dialog_id): self.pause() self.next = ShowDialog(dialog_id, self.screen) self.quit() def unpause(self): self.reset_keys() State.unpause(self) def check_collisions(self): #check bullet collisions b = self.bullets s = self.enemies bl = self.blocks #test if bullet hit player coll = pygame.sprite.spritecollide(self.player, self.bullets, False) for bullet in coll: if bullet.thrower is not self.player and bullet.flying and not \ self.player.invincible: bullet.kill() self.player.get_hit() self.rem_health() #test if bullet hit wall dead = [] for bullet in self.bullets: if bullet.rect.collidelist(bl) > -1: dead.append(bullet) for d in dead: if d.flying: d.kill() #test if bullet hit enemy (ninjats can't shoot each other) coll = pygame.sprite.groupcollide(s, b, False, False) for victim in coll.keys(): #my code gets weird... for murderer in coll[victim]: if murderer.thrower is self.player: victim.die() murderer.kill() #test player vs. enemy collisions coll = pygame.sprite.spritecollide(self.player, s, False) pbottom = self.player.rect.bottom for enemy in coll: if enemy.status == 8: #enemy dead.. ignore! continue etop = enemy.rect.top if self.player.status==5 and pbottom >= etop: #enemy below player and player is falling -> enemy crushed! enemy.die() self.player.bounce() elif not self.player.invincible and not self.player.status==7: #not ducking -> get hit self.player.get_hit() self.rem_health() def rem_health(self): player_data["health"] -= 1 GLOBALS["sfx"][sfx_hit].play() if player_data["health"] <= 0: self.player.die() self.update_ui() def update_ui(self): self.ui_layer.fill((255,0,255)) #health x, y, add = 4, 2, self.heart_img.get_size()[0] + 2 for i in xrange(9): if i < player_data["health"]: #TODO: if i < hp self.ui_layer.blit(self.heart_img, (x,y)) else: self.ui_layer.blit(self.heart_empty_img, (x,y)) x += add #lives x, y, add = 595, 2, self.life_img.get_size()[0] + 2 self.ui_layer.blit(self.life_img, (x,y)) lives = player_data["lives"] life_text = self.fontholder.font.render("x"+str(lives), False, (255,255,255)) self.ui_layer.blit(life_text, (x+add, y+10)) def update_screen(self): for s in self.sprites: s.update_status() if not s.ai: #update player s.update() if not s.status==8 and not self.player.off_screen(): s.check_mapcollisions(self.blocks) trig = s.check_trigcollisions(self.map.triggers) if trig: if trig.name=="exit": self.player.exiting = 1 elif trig.name=="dtrigger": self.show_dialog(trig.params) if trig.params=="tut08": self.player.exiting = -1 self.player.set_image(GLOBALS["nanji"]["idle"][0]) else: player_data["health"] = 9 player_data["lives"] -= 1 if player_data["lives"] > 0: next = Level(self.map_filename, prev=self.prev) else: next = GameOver() self.next = FadeState(self.screen, next.background, next) self.quit() else: #update ai if s.ai_update(self): #throw shuriken? self.bullets.add(Shuriken(s, self.camera)) self.bullets.update() self.camera.update() #draw bg #self.scroll_bg.update(self.player.vel[0]) self.background.blit(self.bg, (0,0)) #draw map and sprites self.lvl_layer.fill((255,0,255)) self.lvl_layer.blit(self.lvl_img, self.camera.pos, [self.camera.pos, GLOBALS["screen_size"]]) self.lvl_layer.set_colorkey((255,0,255)) self.bullets.draw(self.lvl_layer) self.sprites.draw(self.lvl_layer) #pygame.draw.rect(self.lvl_layer, (255,255,0), self.player.colrect, 1) #draw collision rect of nanji if self.player.exiting: #draw semi-black image over dojo entry while entering self.lvl_layer.blit(GLOBALS["objects"]["dojo_entry"], self.map.dojo_pos) self.background.blit(self.lvl_layer, (0,0), [self.camera.pos, GLOBALS["screen_size"]]) self.ui_layer.draw(self.background) self.screen.blit(self.background, (0,0)) pygame.display.flip()
class Ninja(AnimatedKitten): def __init__(self, cam, images): self.all_images = images self.camera = cam AnimatedKitten.__init__(self, self.all_images["idle"], delay=10) self.colrect = pygame.Rect([0, 0, 35, 35]) self.angle = 0 #rotation angle for death animation self.shuriken = None self.idle_timer = Timer(200) self.jump_timer = Timer(20) self.shuriken_timer = Timer(20) self.invincible = 0 self.invincible_timer = Timer( 100) #on hit nanji is invincible for set time self.blink_timer = Timer(2) self.name = "" self.cur_images = "idle" self.speed = 4 self.look = 1 #1 => looking right self.statlist = [ "idle", #0 "walking", #1 "sprinting", #2 "throwing", #3 "jumping", #4 "falling", #5 "landing", #6 "ducking", #7 "dying", #8 ] self.on_ground = [0, 1, 2] self.status = 0 self.fall() def set_image(self, img): #return #DELETEME top = self.rect.top bottom = self.rect.bottom left = self.rect.left right = self.rect.right vx, vy = self.vel AnimatedKitten.set_image(self, img) self.rect.size = self.image.get_size() if vy < 0: self.rect.top = top else: self.rect.bottom = bottom if self.look > 0: self.rect.left = left else: self.rect.right = right self.rect.centerx = self.colrect.centerx self.rect.bottom = self.colrect.bottom self.set_pos(self.rect.topleft) def get_hit(self): if not self.invincible: self.invincible = 1 self.invincible_timer.activate() def bounce(self): #TODO: bouncing looks.. kind of not bouncy self.status = 4 self.jump_timer.activate() self.jump_timer.counter = self.jump_timer.interval - 2 #GLOBALS["sfx"][sfx_bounce].play() def die(self): if self.status == 3 and self.shuriken: self.shuriken.kill() #remove shuriken self.status = 8 GLOBALS["sfx"][sfx_die].play() def change_images(self, name, lookupdate=False): change = self.cur_images != name if change or lookupdate: nname = name nr = self.cur_image counter = self.anim_timer.counter if self.look < 0: nname = name + "_l" AnimatedKitten.set_images(self, self.all_images[nname]) self.cur_images = name if name == "sprinting" or name == "throwing": self.anim_timer.set_interval(3) elif name == "jump": self.anim_timer.set_interval(5) elif name == "landing": self.anim_timer.set_interval(7) else: self.anim_timer.set_interval(10) if lookupdate: self.set_image(self.images[nr]) self.cur_image = nr self.anim_timer.counter = counter #self.stop() def sprint(self): if self.status in self.on_ground: self.status = 2 def unsprint(self): if self.status == 2: self.status = 0 def jump(self): if self.status in self.on_ground: GLOBALS["sfx"][sfx_jump].play() self.status = 4 self.jump_timer.activate() def unjump(self): if self.status == 4 and self.jump_timer.counter > 8: self.status = 5 self.jump_timer.deactivate() def land(self): if self.status == 5: self.status = 6 GLOBALS["sfx"][sfx_land].play() def fall(self): self.status = 5 def throw(self): if (self.status in self.on_ground or self.status==7) and \ self.status != 3 and not self.shuriken_timer.active: self.status = 3 GLOBALS["sfx"][sfx_throw].play() self.mod_vel(vx=0, vy=0) #stand still while throwing!! self.change_images("idle") self.shuriken_timer.activate() return True return False def unthrow(self): if self.status == 3: self.status = 0 def turn(self): self.look = self.look * -1 self.change_images(self.cur_images, lookupdate=True) def duck(self): if self.status in self.on_ground: self.status = 7 def unduck(self): self.status = 0 def mod_vel(self, vx=None, vy=None): nx, ny = self.vel if vx is not None: nx = vx elif nx > 0: nx = 1 elif nx < 0: nx = -1 if vy is not None: ny = vy elif ny > 0: ny = 1 elif ny < 0: ny = -1 self.set_vel((nx, ny)) def set_vel(self, vel): vx, vy = vel sprint, jump = 0, 0 if self.status == 2: sprint = 6 * vx if self.status == 4 and vy < 0: jump = 4 AnimatedKitten.set_vel( self, (vx * self.speed + sprint, vy * self.speed - jump)) def update_status(self): if self.vel[0] > 0 and self.look < 0: self.turn() elif self.vel[0] < 0 and self.look > 0: self.turn() if self.status == 8: #dying :/ self.image = pygame.transform.rotate(self.all_images["jump"][2], self.angle) self.angle += 5 self.pause() return if self.status in self.on_ground: if self.status == 2 and self.vel[0]: self.change_images("sprinting") elif self.vel[0]: self.status = 1 self.change_images("walking") elif not self.cur_images == "idle": self.change_images("idle") self.status = 0 self.idle_timer.activate() self.stop() elif self.status == 7: self.set_vel((0, 0)) self.change_images("ducking") elif self.status == 3: self.change_images("throwing") self.mod_vel(vx=0) if self.cur_image >= len(self.images) -1 and \ self.anim_timer.counter >= self.anim_timer.interval -1: self.unthrow() elif self.status == 6: self.change_images("landing") if self.cur_image >= len(self.images) - 1: self.status = 0 self.change_images("idle") elif self.cur_image == 1 and self.anim_timer.interval < 10: self.anim_timer.set_interval(10) elif self.status == 5: self.mod_vel(vy=1) self.change_images("flight") elif self.status == 4: self.change_images("jump") if self.status in [4, 5] and self.cur_image >= len(self.images) - 1: self.pause() #pause last image of falling and jumping elif self.status == 0: if self.idle_timer.update(): #play idle animation after some time self.play() self.idle_timer.deactivate() else: self.play() if self.shuriken_timer.update(): self.shuriken_timer.deactivate() def update(self): if self.status == 4: if self.cur_image < 3: self.mod_vel(vy=0) else: self.mod_vel(vy=-1) if self.jump_timer.update(): self.jump_timer.counter = 9 self.unjump() #cannibalized from AnimatedKitten.update because that code was a booboo if self.animate and self.anim_timer.update(): self.cur_image += 1 if self.cur_image >= len(self.images): self.cur_image = 0 self.set_image(self.images[self.cur_image]) if self.invincible: if self.blink_timer.update(): if self.image.get_alpha() > 0: self.image = self.image.copy() self.image.set_alpha(0) else: self.image = self.image.copy() self.image.set_alpha(255) if self.invincible_timer.update(): self.image = self.image.copy() self.image.set_alpha(255) self.invincible_timer.deactivate() self.invincible = 0 def set_pos(self, pos): AnimatedKitten.set_pos(self, pos) self.colrect.centerx = self.rect.centerx self.colrect.bottom = self.rect.bottom def check_mapcollisions(self, blocks): bl = blocks vx, vy = self.vel px, py = self.colrect.topleft width, height = self.colrect.size test = 0 if vy == 0: vy = 1 test = 1 colliding = [0, 0] #vertical collisions vrect = pygame.Rect([px, py + vy, width, height]) blocks = vrect.collidelistall(bl) for nr in blocks: block = bl[nr] colliding[1] = vy if vy > 0 and vrect.bottom > block.top and not self.status == 6: if not test: self.land() self.colrect.bottom = block.top self.mod_vel(vy=0) elif vy < 0 and vrect.top < block.bottom: self.unjump() self.colrect.top = block.bottom self.mod_vel(vy=0) if test and not blocks and not self.status == 4 and not self.status == 6: #fall unless you are jumping or landing self.fall() elif not blocks: self.colrect.top += vy #kk, move along #horizontal collisions hrect = pygame.Rect([px + vx, py, width, height]) blocks = hrect.collidelistall(bl) for nr in blocks: block = bl[nr] colliding[0] = vx if vx > 0 and hrect.right > block.left: self.colrect.right = block.left self.mod_vel(vx=0) elif vx < 0 and hrect.left < block.right: self.colrect.left = block.right self.mod_vel(vx=0) if not blocks: self.colrect.left += vx #gogo little kitteh! self.rect.midbottom = self.colrect.midbottom self.set_pos(self.rect.topleft) return colliding def off_screen(self): #returns true if character off screen off_x, off_y = self.camera.pos real_x, real_y = self.pos[0] - off_x, self.pos[1] - off_y screen_x, screen_y = GLOBALS["screen_size"] w, h = self.get_size() #set larger screen to keep updating ninjats w += 600 h += 100 off_x -= 300 off_y -= 50 return real_x > screen_x + w or real_y > screen_y + h or real_x < -w or real_y < -h def check_trigcollisions(self, trigger): pass #keep this.