import math, random from art import TileIter from game_object import GameObject from game_util_objects import Player, StaticTileBG from collision import CST_NONE, CST_CIRCLE, CST_AABB, CST_TILE, CT_NONE, CT_GENERIC_STATIC, CT_GENERIC_DYNAMIC, CT_PLAYER, CTG_STATIC, CTG_DYNAMIC class MazeBG(StaticTileBG): z = -0.1 class MazeNPC(GameObject): art_src = 'npc' use_art_instance = True col_radius = 0.5 collision_shape_type = CST_CIRCLE collision_type = CT_GENERIC_STATIC bark = 'Well hello there!' def started_colliding(self, other): if not isinstance(other, Player): return self.world.hud.post_msg(self.bark) def pre_first_update(self): self.z = 0.1 # TODO: investigate why this random color set doesn't seem to work random.seed(self.name) random_color = random.randint(3, len(self.art.palette.colors)) for art in self.arts.values(): art.set_all_non_transparent_colors(random_color) class MazeBaker(MazeNPC): bark = 'Sorry, all outta bread today!' class MazeCritter(MazeNPC): "dynamically-spawned NPC that wobbles around" collision_type = CT_GENERIC_DYNAMIC should_save = False move_rate = 0.25 bark = 'wheee!' def update(self): # skitter around randomly x, y = (random.random() * 2) - 1, (random.random() * 2) - 1 x *= self.move_rate y *= self.move_rate self.move(x, y) GameObject.update(self) class MazePickup(GameObject): collision_shape_type = CST_CIRCLE collision_type = CT_GENERIC_DYNAMIC col_radius = 0.5 hold_offset_y = 1.2 consume_on_use = True sound_filenames = {'pickup': 'pickup.ogg'} def __init__(self, world, obj_data=None): GameObject.__init__(self, world, obj_data) self.holder = None def started_colliding(self, other): if not isinstance(other, Player): return if self is other.held_object: return other.pick_up(self) def stopped_colliding(self, other): if not isinstance(other, Player): return if self is not other.held_object: self.enable_collision() def picked_up(self, new_holder): self.holder = new_holder self.world.hud.post_msg('got %s!' % self.display_name) self.disable_collision() self.play_sound('pickup') def used(self, user): if 'used' in self.sound_filenames: self.play_sound('used') if self.consume_on_use: self.destroy() def destroy(self): if self.holder: self.holder.held_object = None self.holder = None GameObject.destroy(self) def update(self): GameObject.update(self) if not self.holder: return # if held, shadow holder self.x = self.holder.x # bob slightly above holder's head bob_y = math.sin(self.world.get_elapsed_time() / 100) / 10 self.y = self.holder.y + self.hold_offset_y + bob_y self.z = self.holder.z class MazeKey(MazePickup): art_src = 'key' display_name = 'a gold key' used_message = 'unlocked!' class MazeAx(MazePickup): art_src = 'ax' display_name = 'an ax' consume_on_use = False used_message = 'chop!' # TODO: see if there's a way to add to MazePickup's sound dict here :/ sound_filenames = {'pickup': 'pickup.ogg', 'used': 'break.ogg'} class MazePortalKey(MazePickup): art_src = 'artifact' display_name = 'the Artifact of Zendor' used_message = '!!??!?!!?!?!?!!' consume_on_use = False sound_filenames = {'pickup': 'artifact.ogg', 'used': 'portal.ogg'} def update(self): MazePickup.update(self) ch, fg, bg, xform = self.art.get_tile_at(0, 0, 0, 0) # before artifact is held, fluctuate char if not self.holder: ch += 1 ch %= self.art.charset.last_index if ch == 0: ch = 1 # always fluctuate its color fg += 1 fg %= len(self.art.palette.colors) self.art.set_tile_at(0, 0, 0, 0, ch, fg, bg, xform) class MazeLock(StaticTileBG): art_src = 'lock' collision_shape_type = CST_CIRCLE collision_type = CT_GENERIC_DYNAMIC col_radius = 0.5 mass = 0.0 key_type = MazeKey def started_colliding(self, other): if not isinstance(other, Player): return if other.held_object and type(other.held_object) is self.key_type: self.unlocked(other) else: self.world.hud.post_msg('blocked - need %s!' % self.key_type.display_name) def unlocked(self, other): self.disable_collision() self.visible = False other.use_item() class MazeBlockage(MazeLock): art_src = 'debris' key_type = MazeAx class MazePortalGate(MazeLock): art_src = 'portalgate' key_type = MazePortalKey collision_shape_type = CST_TILE collision_type = CT_GENERIC_STATIC def update(self): MazeLock.update(self) if self.collision_type == CT_NONE: if not self.art.is_script_running('evap'): self.art.run_script_every('evap') return # cycle non-black colors BLACK = 1 LAST_COLOR = len(self.art.palette.colors) - 1 for frame, layer, x, y in TileIter(self.art): ch, fg, bg, xform = self.art.get_tile_at(frame, layer, x, y) # alternate wedge characters if ch == 148: ch = 149 elif ch == 149: ch = 148 if fg != BLACK and fg != 0: fg += 1 if fg > LAST_COLOR: fg = 2 if bg != BLACK and bg != 0: bg += 1 if bg > LAST_COLOR: bg = 2 self.art.set_tile_at(frame, layer, x, y, ch, fg, bg, xform) class MazePortal(GameObject): art_src = 'portal' def update(self): GameObject.update(self) if self.app.updates % 2 != 0: return ramps = {11: 10, 10: 3, 3: 11} for frame, layer, x, y in TileIter(self.art): ch, fg, bg, xform = self.art.get_tile_at(frame, layer, x, y) fg = ramps.get(fg, None) self.art.set_tile_at(frame, layer, x, y, ch, fg, bg, xform) class MazeStandingNPC(GameObject): art_src = 'npc' col_radius = 0.5 collision_shape_type = CST_CIRCLE collision_type = CT_GENERIC_DYNAMIC bark = 'Well hello there!' def started_colliding(self, other): if not isinstance(other, Player): return self.world.hud.post_msg(self.bark)