import math import random from art import TileIter from collision import ( CST_CIRCLE, CST_TILE, CT_GENERIC_DYNAMIC, CT_GENERIC_STATIC, CT_NONE, ) from game_object import GameObject from game_util_objects import Player, StaticTileBG 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) 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)