def dragonweed_pull(actor, target, context): if target.fighter.hp > 0: ui.message( "The dragonweed's stem lashes out at %s!" % syntax.name(target), libtcod.dark_green) result = combat.attack_ex(actor.fighter, target, 0, accuracy_modifier=1.5, damage_multiplier=0.75, verb=('pull', 'pulls')) if result == 'hit' and target.fighter is not None: if 'displacement' in target.fighter.immunities: if fov.player_can_see(target.x, target.y): ui.message( '%s %s.' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('resist', 'resists'))), libtcod.gray) return 'success' beam = main.beam(actor.x, actor.y, target.x, target.y) pull_to = beam[max(len(beam) - 3, 0)] target.set_position(pull_to[0], pull_to[1]) if main.roll_dice('1d10') <= 5: target.fighter.apply_status_effect( effects.immobilized(duration=2), context['save_dc'], actor)
def zombie_on_hit(attacker, target, damage): if target.fighter is None: return if libtcod.random_get_int(0, 0, 100) < 20: ui.message( '%s grabs %s! %s cannot move!' % (syntax.name(attacker).capitalize(), syntax.name(target), syntax.pronoun(target).capitalize()), libtcod.yellow) target.fighter.apply_status_effect(effects.immobilized(3))
def knock_back(actor, target): # check for resistance if 'displacement' in target.fighter.immunities: if fov.player_can_see(target.x, target.y): ui.message( '%s %s.' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('resist', 'resists'))), libtcod.gray) return 'resisted' # knock the target back one space. Stun it if it cannot move. direction = target.x - actor.x, target.y - actor.y # assumes the instance is adjacent stun = False against = '' against_tile = main.current_map.tiles[target.x + direction[0]][target.y + direction[1]] if against_tile.blocks and not against_tile.is_pit: stun = True against = main.current_map.tiles[target.x + direction[0]][target.y + direction[1]].name elif against_tile.elevation != target.elevation and against_tile.tile_type != 'ramp' and \ main.current_map.tiles[target.x][target.y] != 'ramp': stun = True against = 'cliff' else: for obj in main.current_map.objects: if obj.x == target.x + direction[ 0] and obj.y == target.y + direction[1] and obj.blocks: stun = True against = obj.name break if stun: # stun the target if target.fighter.apply_status_effect(effects.stunned(duration=2)): ui.message( '%s %s with the %s, stunning %s!' % (syntax.name(target).capitalize(), syntax.conjugate(target is actor, ('collide', 'collides')), against, syntax.pronoun(target, objective=True)), libtcod.gold) else: ui.message( '%s %s knocked backwards.' % (syntax.name(target).capitalize(), syntax.conjugate(target is actor, ('are', 'is'))), libtcod.gray) target.set_position(target.x + direction[0], target.y + direction[1]) main.render_map() libtcod.console_flush()
def heal_other(actor, target, context): if actor is player.instance: ui.message('Yo implement this', libtcod.red) return 'failure' ui.render_explosion(target.x, target.y, 0, libtcod.lightest_green, libtcod.green) amount = main.roll_dice('3d4') target.fighter.heal(amount) ui.message( '%s %s %s for %d damage.' % (syntax.name(actor).capitalize(), syntax.conjugate(actor is player.instance, ('heal', 'heals')), syntax.name(target, reflexive=actor), amount), libtcod.green) return 'success'
def spawn_spiders(actor, target, context): #Filthy hackery to add some state if not hasattr(actor, 'summons'): actor.summons = [] for s in actor.summons: # clear dead things from summoned list if not s.fighter: actor.summons.remove(s) if len(actor.summons) < context['max_summons']: summon_tiles = [] for y in range(3): for x in range(3): pos = actor.x - 1 + x, actor.y - 1 + y if main.in_bounds(pos[0], pos[1]) and not main.is_blocked( pos[0], pos[1]): summon_tiles.append(pos) summon_count = main.roll_dice(context['summons_per_cast']) for i in range(summon_count): if len(summon_tiles) > 0 and len( actor.summons) < context['max_summons']: pos = summon_tiles[libtcod.random_get_int( 0, 0, len(summon_tiles) - 1)] spawn = main.spawn_monster('monster_tunnel_spider', pos[0], pos[1]) ui.message( 'A ' + spawn.name + " crawls from beneath %s cloak." % syntax.name(actor, possesive=True), actor.color) actor.summons.append(spawn) summon_tiles.remove(pos) return 'success' return 'cancelled'
def blastcap_explode(blastcap, context): blastcap.fighter = None main.current_map.fighters.remove(blastcap) ui.render_explosion(blastcap.x, blastcap.y, 1, libtcod.gold, libtcod.white, distance_h='manhattan') ui.message('The blastcap explodes with a BANG, stunning nearby creatures!', libtcod.gold) for obj in main.current_map.fighters: if main.is_adjacent_orthogonal(blastcap.x, blastcap.y, obj.x, obj.y): if obj.fighter.apply_status_effect( effects.stunned(duration=context['duration'])): ui.message( '%s %s stunned!' % (syntax.name(obj).capitalize(), syntax.conjugate(obj is player.instance, ('are', 'is'))), libtcod.gold) if ui.selected_monster is blastcap: main.changed_tiles.append((blastcap.x, blastcap.y)) ui.selected_monster = None ui.auto_target_monster() blastcap.destroy() return
def disarm(target): if target is None or target.fighter is None: return 'failure' weapon = main.get_equipped_in_slot(target.fighter.inventory, 'right hand') if weapon is None: return 'failure' weapon.dequip(no_message=True) possible_tiles = [] for x in range(target.x - 1, target.x + 2): for y in range(target.y - 1, target.y + 2): if not main.is_blocked( x, y, from_coord=(target.x, target.y), movement_type=1): possible_tiles.append((x, y)) if len(possible_tiles) == 0: selected_tile = main.find_closest_open_tile(target.x, target.y) else: selected_tile = possible_tiles[libtcod.random_get_int( 0, 0, len(possible_tiles) - 1)] weapon.owner.item.drop(no_message=True) weapon.owner.x = selected_tile[0] weapon.owner.y = selected_tile[1] ui.message( '%s %s disarmed!' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('are', 'is'))), libtcod.red) return 'success'
def strangleweeds(actor, target, context): hit = False for f in main.get_fighters_in_burst( actor.x, actor.y, context['range'], actor, lambda o: o.fighter.team != actor.fighter.team): tile = main.current_map.tiles[f.x][f.y] if tile.tile_type == 'grass floor': if actor is player.instance or fov.player_can_see(f.x, f.y): ui.message( 'Writhing vines grip %s and hold %s in place!' % (syntax.name(f), syntax.pronoun(f)), spells.essence_colors['life']) f.fighter.apply_status_effect( effects.immobilized(duration=context['duration'])) f.fighter.apply_status_effect( effects.StatusEffect( 'strangleweeds', time_limit=context['duration'], color=libtcod.lime, on_tick=strangleweed_on_tick, message='The strangleweeds crush you!', description='This unit will take damage every turn', cleanseable=True)) hit = True if hit: return 'success' else: if actor is player.instance: ui.message("You can't see any susceptible targets.", libtcod.gray) return 'cancelled'
def flash_frost(actor, target, context): target.fighter.apply_status_effect(effects.frozen(5)) if actor is player.instance or fov.player_can_see(target.x, target.y): ui.message( '%s %s frozen solid!' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('are', 'is'))), spells.essence_colors['cold'])
def teleport(actor, x, y): if actor is None: actor = player.instance if actor is player.instance or fov.player_can_see(actor.x, actor.y): ui.message( '%s %s in a crackle of magical energy!' % (syntax.name(actor).capitalize(), syntax.conjugate(actor is player.instance, ('vanish', 'vanishes'))), spells.essence_colors['arcane']) actor.set_position(x, y) if actor is not player.instance and fov.player_can_see(actor.x, actor.y): ui.message( '%s appears out of thin air!' % syntax.name(actor).capitalize(), spells.essence_colors['arcane']) return 'success'
def on_hit_knockback(attacker, target, damage, force=6): if target.fighter is None: return if 'displacement' in target.fighter.immunities: if fov.player_can_see(target.x, target.y): ui.message( '%s %s.' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('resist', 'resists'))), libtcod.gray) return diff_x = target.x - attacker.x diff_y = target.y - attacker.y if diff_x > 0: diff_x = diff_x / abs(diff_x) if diff_y > 0: diff_y = diff_y / abs(diff_y) direction = (diff_x, diff_y) steps = 0 while steps <= force: steps += 1 against = main.get_objects(target.x + direction[0], target.y + direction[1], lambda o: o.blocks) if against is None or len(against) == 0: against = syntax.name( main.current_map.tiles[target.x + direction[0]][target.y + direction[1]]) else: against = 'the ' + against.name if not target.move(direction[0], direction[1]): # Could not move damage = combat.roll_damage('%dd4' % steps, '0d0', target.fighter.armor, 0, 'budgeoning', 1.0, target.fighter.resistances) ui.message( '%s %s backwards and collides with %s, taking %d damage.' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('fly', 'flies')), against, damage), libtcod.gray) target.fighter.take_damage(damage, attacker=attacker) steps = force + 1
def step_on_blightweed(weed, obj): if obj.fighter: obj.fighter.time_since_last_damaged = 0 if obj.fighter.armor > 0: obj.fighter.get_shredded(1) if fov.player_can_see(obj.x, obj.y): ui.message( 'The blightweed thorns shred %s armor!' % syntax.name(obj, possesive=True), libtcod.desaturated_red)
def web_bomb(actor, target, context): if actor is player.instance: ui.message('Yo implement this', libtcod.red) return 'failure' main.tunnel_spider_spawn_web(target) if actor is player.instance or fov.player_can_see(target.x, target.y): ui.message('%s summons spiderwebs.' % syntax.name(actor).capitalize(), actor.color) return 'success'
def hardness(target): target.fighter.shred = 0 target.fighter.apply_status_effect(effects.stoneskin(21)) if target is player.instance or fov.player_can_see(target.x, target.y): ui.message( '%s armor is repaired and %s skin becomes as hard as stone.' % (syntax.name(target, possesive=True).capitalize(), syntax.pronoun(target, possesive=True)), spells.essence_colors['earth']) return 'success'
def haste(actor, target, context): target.fighter.apply_status_effect( effects.hasted(duration=context['duration'])) ui.render_explosion(target.x, target.y, 0, libtcod.lightest_fuchsia, libtcod.fuchsia) ui.message( '%s %s hasted.' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('are', 'is'))), spells.essence_colors['arcane']) return 'success'
def berserk_self(actor, target, context): if not actor.fighter.has_status( 'berserk') and not actor.fighter.has_status('exhausted'): actor.fighter.apply_status_effect(effects.berserk()) if actor is not player.instance: ui.message( '%s %s!' % (syntax.name(actor).capitalize(), syntax.conjugate(False, ('roar', 'roars'))), libtcod.red) return 'success' else: if actor is player.instance: ui.message("You cannot go berserk right now.", libtcod.yellow)
def sh_raise(self): self.sh_timer = 0 if not self.raised and (self.holder is player.instance or fov.monster_can_see_object( self.holder, player.instance)): ui.message( '%s %s %s shield.' % (syntax.name(self.holder).capitalize(), syntax.conjugate(self.holder is player.instance, ('raise', 'raises')), syntax.pronoun(self.holder, possesive=True)), libtcod.blue) self.raised = True self.sh_points = self.sh_max
def lightning_storm(actor, target, context): for tile in target: obj = main.get_monster_at_tile(tile[0], tile[1]) ui.render_explosion(tile[0], tile[1], 0, libtcod.light_green, libtcod.lightest_blue, chr(251)) if obj is not None and obj is not actor: if fov.player_can_see(tile[0], tile[1]) or actor is player.instance: ui.message( '%s is struck by a bolt of lightning!' % syntax.name(obj).capitalize(), libtcod.lightest_blue) combat.attack_magical(actor.fighter, obj, 'ability_lightning_storm')
def frog_tongue(actor, target, context): if target.fighter.hp > 0: ui.message("The frog's tongue lashes out at %s!" % syntax.name(target), libtcod.dark_green) result = combat.attack_ex(actor.fighter, target, 0, accuracy_modifier=1.5, damage_multiplier=1.5, verb=('pull', 'pulls')) if result == 'hit': if 'displacement' in target.fighter.immunities: if fov.player_can_see(target.x, target.y): ui.message( '%s %s.' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('resist', 'resists'))), libtcod.gray) return 'success' beam = main.beam(actor.x, actor.y, target.x, target.y) pull_to = beam[max(len(beam) - 3, 0)] target.set_position(pull_to[0], pull_to[1])
def confuse(actor, target, context): import consts if target.fighter.apply_status_effect( effects.StatusEffect( 'confusion', consts.CONFUSE_NUM_TURNS, color=libtcod.pink, )): ui.message( '%s %s confused!' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('are', 'is'))), libtcod.light_blue)
def silence(actor, target, context): if target.fighter.apply_status_effect( effects.silence(duration=context.get('duration', 10)), dc=context.get('base_dc', 20) + actor.fighter.spell_power(['arcane']), source_fighter=actor.fighter, supress_message=True): if actor is player.instance or target is player.instance or fov.player_can_see( target.x, target.y): ui.message( '%s %s silenced!' % (syntax.name(target).capitalize(), syntax.conjugate(target is player.instance, ('are', 'is'))), libtcod.light_blue)
def throw_net(actor, target, context): if actor is player.instance: ui.message('Yo implement this', libtcod.red) return 'failure' dist = actor.distance_to(target) if dist > context['range']: return 'cancelled' ui.message( '%s %s a net at %s.' % (syntax.name(actor).capitalize(), syntax.conjugate(actor is player.instance, ('throw', 'throws')), syntax.name(target)), libtcod.gold) ui.render_projectile((actor.x, actor.y), (target.x, target.y), libtcod.gold, character='#') target.fighter.apply_status_effect( effects.immobilized(duration=context['duration']), context['save_dc'], source_fighter=actor) return 'success'
def fungal_growth(actor, target, context): x, y = target corpse = main.get_objects(x, y, lambda o: o.is_corpse) if len(corpse) == 0: ui.message('No suitable corpses here.', libtcod.gray) return 'cancelled' target = corpse[0] if not target.is_corpse: return 'failure' main.spawn_monster('monster_blastcap', target.x, target.y) if actor is player.instance or fov.player_can_see(target.x, target.y): ui.message('A blastcap grows from %s.' % syntax.name(target), spells.essence_colors['life']) target.destroy() return 'success'
def dig_line(x, y, dx, dy, length, actor=None): if actor is None: actor = player.instance result = 'failure' for i in range(length): d = dig(x + dx * i, y + dy * i) if result != 'success': result = d if result == 'success': if actor is player.instance or fov.player_can_see(actor.x, actor.y): ui.message('The earth parts before %s.' % syntax.name(actor), spells.essence_colors['earth']) else: if actor is player.instance: ui.message('There is nothing to dig in that direction.', libtcod.gray) return result
def scum_glob_death(glob, context): ui.message('%s divides!' % syntax.name(glob).capitalize(), libtcod.red) pos = glob.x, glob.y glob.fighter = None glob.destroy() for i in range(3): spawn = main.find_closest_open_tile(pos[0], pos[1]) main.spawn_monster('monster_scum_glob_small', spawn[0], spawn[1]) tile = main.current_map.tiles[spawn[0]][spawn[1]] if not tile.is_water and not tile.tile_type == 'oil' and not tile.is_ramp: tile.tile_type = 'oil' if ui.selected_monster is glob: main.changed_tiles.append(pos) ui.selected_monster = None ui.auto_target_monster()
def raise_zombie(actor, target, context): corpse = None for tile in target: corpses_here = main.get_objects( tile[0], tile[1], lambda o: o.name.startswith('remains of')) if len(corpses_here) > 0: corpse = corpses_here[0] break if corpse is not None: ui.message( 'A dark aura emanates from %s... a corpse walks again.' % syntax.name(actor), libtcod.dark_violet) main.raise_dead(actor, corpse) return 'rasied-zombie' else: return 'didnt-take-turn'
def great_dive(actor, target, context): ui.message("{} {} into the ground!".format( syntax.name(actor).capitalize(), syntax.conjugate(actor is player.instance, ['slam', 'slams']))) for obj in main.current_map.fighters: if (obj.x, obj.y) in target: combat.attack_ex(actor.fighter, obj, 0) x, y = context['origin'] if not main.is_blocked(x, y): actor.set_position(x, y) else: for t in target: if not main.is_blocked(t[0], t[1]): actor.set_position(t[0], t[1]) break
def bramble(actor, target, context): ui.message( 'Thorny brambles spring from %s fingertips!' % syntax.name(actor, possesive=True), spells.essence_colors['life']) for tile in target: _bramble = main.GameObject( tile[0], tile[1], 'x', 'bramble', libtcod.dark_lime, on_step=bramble_on_step, summon_time=context['duration_base'] + main.roll_dice(context['duration_variance'])) _bramble.summoner = actor _bramble.spell_power = actor.fighter.spell_power(['life']) main.current_map.add_object(_bramble) return 'success'
def boomerang(actor, target, context): sprites = ['<', 'v', '>', '^'] ui.render_projectile((actor.x, actor.y), (target.x, target.y), libtcod.yellow, sprites) attack_result = actor.fighter.attack(target) if attack_result == 'failed': return 'didnt-take-turn' catch_skill = 30 if actor.player_stats: catch_skill = actor.player_stats.agi if main.roll_dice('1d' + str(catch_skill)) >= 10: #catch boomerang ui.render_projectile((target.x, target.y), (actor.x, actor.y), libtcod.yellow, sprites) if actor is player.instance: ui.message('You catch the boomerang as it returns to you', libtcod.gray) else: possible_tiles = [] for y in range(actor.y - 2, actor.y + 2): for x in range(actor.x - 2, actor.x + 2): if x >= 0 and y >= 0 and x < consts.MAP_WIDTH and y < consts.MAP_HEIGHT and not main.is_blocked( x, y): possible_tiles.append((x, y)) if len(possible_tiles) == 0: selected_tile = main.find_closest_open_tile(target.x, target.y) else: selected_tile = possible_tiles[libtcod.random_get_int( 0, 0, len(possible_tiles) - 1)] ui.render_projectile((target.x, target.y), (selected_tile[0], selected_tile[1]), libtcod.yellow, sprites) weapon = main.get_equipped_in_slot(actor.fighter.inventory, 'right hand') weapon.owner.item.drop(no_message=True) weapon.owner.x = selected_tile[0] weapon.owner.y = selected_tile[1] if actor is player.instance or fov.player_can_see(actor.x, actor.y): ui.message( '%s boomerang falls to the ground.' % syntax.name(target, possesive=True).capitalize(), libtcod.gray) libtcod.console_flush()
def bomb_beetle_death(beetle, context): ui.message('%s is dead!' % syntax.name(beetle).capitalize(), libtcod.red) beetle.char = 149 beetle.color = libtcod.black beetle.blocks = True beetle.fighter = None beetle.behavior = None beetle.name = 'beetle bomb' beetle.description = 'The explosive carapace of a blast beetle. In a few turns, it will explode!' beetle.bomb_timer = 3 beetle.on_tick = bomb_beetle_corpse_tick if hasattr(beetle, 'recoverable_ammo'): main.drop_ammo(beetle.x, beetle.y, beetle.recoverable_ammo) del beetle.recoverable_ammo main.current_map.fighters.remove(beetle) if ui.selected_monster is beetle: main.changed_tiles.append((beetle.x, beetle.y)) ui.selected_monster = None ui.auto_target_monster()