class Corpse(Entity): obstructs = True teamed_images = images.load_teamed("units", "corpse.png") def __init__(self, pos, team): set_teamed_skin(self, team) super().__init__(pos)
class Golem(MeleeUnit): teamed_torso_frames = images.load_teamed_anim("golem_torso", 2) teamed_arm_frames = images.load_teamed_anim("golem_arm", 3) teamed_toes = images.load_teamed("units", "golem_toes.png") def __init__(self, pos, team): self.torso_frames = self.teamed_torso_frames[team] self.arm_frames = self.teamed_arm_frames[team] self.toes = self.teamed_toes[team] self.lap_time = 450 # Base x1.25 self.fight_windup_time = 180 self.recover_time = 360 + 180 # Exactly archer reload speed self.wounded = False super().__init__(pos, team) def draw(self, pos): # Might make this the standard drawing function at some pt frame = int(not self.wounded) self._draw(pos, self.torso_frames[frame]) if 0 == self.cooldowns[cd_move]: self._draw(pos, self.toes) frame = (self.fight_windup_time - self.cooldowns[cd_fight]) * 2 // self.fight_windup_time self._draw(pos, self.arm_frames[frame]) def should_chill(self): if self.wounded: if not self.charge(cd_recover): return True # Most other things happen in a custom task, but heck, # it's not like this is racing any other conditions # or needs to be exclusive with anything else self.wounded = False self.dirty() self.cooldowns[cd_recover] = self.recover_time self.charge(cd_fight) and self.charge(cd_move) return True def take_hit(self): if self.wounded: tasks.Die(self) else: self.dirty() self.wounded = True self.ai.queue_immediately() def move(self, pos): super().move(pos) self.cooldowns[cd_recover] = self.recover_time def fight(self, target): super().fight(target) self.cooldowns[cd_recover] = self.recover_time
class Exploder(Unit): teamed_toes = images.load_teamed("units", "exploder_toes.png") teamed_torso_frames = images.load_teamed_anim("exploder_torso", 2) def __init__(self, pos, team): self.torso_frames = self.teamed_torso_frames[team] self.toes = self.teamed_toes[team] # Same move speed as Berserker self.lap_time = 288 self.fight_windup_time = 0 self.dying = False super().__init__(pos, team) def draw(self, pos): frame = int(not self.dying) self._draw(pos, self.torso_frames[frame]) if 0 == self.cooldowns[cd_move]: self._draw(pos, self.toes) def take_hit(self): if self.dying: return self.dying = True self.dirty() tasks.Die(self, self.lap_time, tasks.SLOW_PATIENCE) def die(self): if self.dead: # This is possible if we entered a castle while dying, # which immediately disintegrates us. # Usually this isn't a problem, but explode() assumes # a not-None position. Of course, I could check that # directly, but I'd rather it fail loudly if the # assumption that living Entities have a position is # violated. return corpse = ExploderCorpse(self.pos, self.team) tasks.schedule(tasks.Move(corpse, None), 90) pos = self.pos tasks.Lambda(lambda: explode(pos), 60, tasks.THINK_PATIENCE) self.disintegrate() def get_locs_in_range(self): return []
class Castle(Actor): teamed_sprites = images.load_teamed("structures", "castle.png") def __init__(self, pos, team=6): self.image = self.teamed_sprites[team] self.unit_type = None self.pending_team = None self.charged = False super().__init__(pos, team) SimpleAi(self) def change_team(self, team): if self.pos != None: actor_counts[team] += 1 actor_counts[self.team] -= 1 self.team = team self.image = self.teamed_sprites[team] get_tile(self.pos).handle_activity(self) self.dirty() def take_hit(self): # Don't need to do anything else, actually. # When it comes time to produce the unit, # nothing will happen because we're on the ghost team. # Becoming any other team will reset the spawn "cooldown". self.change_team(6) def convert(self, team, unit_type): if self.pending_team == None: self.pending_team = team self.pending_unit_type = unit_type self.has_task(tasks.Lambda(self.resolve_conversion, 0, 0)) elif self.pending_team != team or self.pending_unit_type != unit_type: self.pending_team = 6 self.pending_unit_type = None def resolve_conversion(self): if self.pending_team == self.team and self.pending_unit_type == self.unit_type: return for t in self.tasks.copy(): if isinstance(t, tasks.BlankTask): t.cancel() # maybe not strictly necessary, but it marks the tile as having activity etc self.change_team(self.pending_team) self.pending_team = None self.unit_type = self.pending_unit_type self.charged = False # TODO Should this have a base impl at the Actor level? def should_chill(self): if self.tasks or self.team == 6: return True if not self.charged: self.charged = True cooldown = 360 * 3 if self.unit_type == Sword else 360 * 5 self.has_task(tasks.BlankTask(cooldown)) return True for x in get_tile(self.pos).contents: if x.obstructs and x != self: self.ai.watch(self.pos) return True self.charged = False # TODO I am being lazy, maybe make this more legible? self.has_task( tasks.Lambda( lambda: ControlledAi(self.unit_type(self.pos, self.team)), 0, tasks.ACT_PATIENCE))
class ExploderCorpse(Corpse): teamed_images = images.load_teamed("units", "exploder_corpse.png")
"""end board""" """rendering""" def tile_to_screen(pos): (x, y) = pos return ((screen_offset_x + TILE_WIDTH * x + int(TILE_WIDTH / 2) * y), (screen_offset_y + TILE_HEIGHT * y)) def draw_tile(pos): for entity in get_tile(pos).contents: entity.draw(pos) ready_badges = images.load_teamed("icons", "team_ready.png") waiting_badges = images.load_teamed("icons", "team_waiting.png") dead_badges = images.load_teamed("icons", "team_skull_1.png") badge_margin = ready_badges[0].get_width() // 2 badge_spacing = ready_badges[0].get_width() + badge_margin * 2 #Surface used to darken the screen before drawing the overlay overlay_bg = pg.Surface((SCREEN_WIDTH, SCREEN_HEIGHT)) overlay_bg.set_alpha(128) overlay_bg.fill(0) overlay_active = False screen_dirty = True dirty_tiles = set() def redraw():