def mass_heal(actor, target, context): for unit in target: unit.fighter.apply_status_effect(effects.regeneration()) amount = int(round(0.25 * unit.fighter.max_hp)) unit.fighter.heal(amount) ui.message( "{} {} healed!".format( syntax.conjugate(unit is player.instance, ['You', unit.name]), syntax.conjugate(unit is player.instance, ['were', 'was'])), libtcod.white) return 'success'
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 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 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 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 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 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 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 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 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 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 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 corpse_dance(actor, target, context): x, y = target ui.render_explosion(x, y, context['radius'], libtcod.violet, libtcod.light_yellow) ui.message("{} calls the dead to dance!".format( syntax.conjugate(actor is player.instance, ["You", actor.name.capitalize()]))) for o in main.get_objects(x, y, None, context['radius']): if o is not None and o.is_corpse: main.raise_dead(actor, o) if o.fighter is not None and o.fighter.team == actor.fighter.team and o.fighter.subtype == 'undead': o.fighter.apply_status_effect( effects.swiftness(context['buff_duration'])) o.fighter.apply_status_effect( effects.berserk(context['buff_duration']))
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 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 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 reeker_breath(actor, target, context): #TODO: Upgrade this to use auto targeting x = target.x y = target.y tiles = main.cone(actor.x, actor.y, x, y, max_range=context['range']) if tiles is None or len(tiles) == 0 or tiles[0] is None: return 'cancelled' if fov.player_can_see(target.x, target.y) or actor is player.instance: ui.message( '%s %s a cloud of acrid fumes!' % (syntax.name(actor).capitalize(), syntax.conjugate(actor is player.instance, ('breathe', 'breathes'))), libtcod.fuchsia) for tile in tiles: main.create_reeker_gas(tile[0], tile[1], duration=main.roll_dice('1d6') + 3) for obj in main.current_map.fighters: if obj.x == tile[0] and obj.y == tile[1]: combat.attack_magical(actor.fighter, obj, 'ability_reeker_breath')
def summon_equipment_on_tick(ticker): dead_flag = False dropped = False owner = ticker.owner if not owner or not owner.fighter: dead_flag = True elif not ticker.equipment.equipment.is_equipped: if owner is player.instance or fov.player_can_see(owner.x, owner.y): ui.message( 'The %s fades away as %s %s it from %s grasp.' % (ticker.equipment.name.title(), syntax.name(owner), syntax.conjugate(owner is player.instance, ('release', 'releases')), syntax.pronoun(owner, possesive=True)), libtcod.light_blue) dead_flag = True dropped = True elif ticker.ticks > ticker.max_ticks: dead_flag = True if owner is player.instance or fov.player_can_see(owner.x, owner.y): ui.message( "The %s fades away as it's essence depletes." % ticker.equipment.name.title(), libtcod.light_blue) if dead_flag: ticker.dead = True if ticker.equipment is not None: if hasattr(ticker.equipment.equipment, 'raised'): ticker.equipment.equipment.sh_raise() ticker.equipment.item.drop(no_message=True) ticker.equipment.destroy() if owner and owner.fighter: owner.fighter.remove_status('summoned equipment') if not dropped and owner and owner.fighter: if ticker.old_equipment is not None: ticker.old_equipment.equip() if ticker.old_left is not None: ticker.old_left.equip()
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 invoke_ability(ability_key, actor, target_override=None, spell_context=None): import abilities if actor is None: raise Exception('Missing parameter: actor') info = abilities.data[ability_key] if info is None: raise Exception("Invalid action key: {}".format(ability_key)) function = info.get('function') if function is None: raise Exception("No function for action {}".format(ability_key)) if spell_context is not None: for key in spell_context.keys(): if spell_context[key] is not None: info[key] = spell_context[key] #info = dict(info.items() + spell_context.items()) if 'require_weapon' in info.keys(): weapon = main.get_equipped_in_slot(actor.fighter.inventory, 'right hand') if weapon is None or weapon.subtype != info['require_weapon']: ui.message( 'You need a {} to use this ability'.format( info['require_weapon']), libtcod.gray) return 'didnt-take-turn' if 'stamina_cost' in info.keys(): if actor is player.instance and actor.fighter.stamina < info[ 'stamina_cost']: ui.message("You don't have enough stamina to use that ability.", libtcod.gray) return 'didnt-take-turn' if 'cast_time' in info.keys(): pretargeted = False if info.get('warning', False): targets = _get_ability_target(actor, info, target_override) if targets is None: return 'didnt-take-turn' info['warning_particles'] = _spawn_warning(actor, targets) pretargeted = targets if 'pre_cast' in info.keys(): info['pre_cast'](actor, target_override) elif not info.get('suppress_cast_notif', False): ui.message_flush( syntax.conjugate( actor is player.instance, ['You begin', actor.name.capitalize() + ' begins']) + ' to cast ' + info['name']) delegate = lambda: _invoke_ability_continuation( info, actor, target_override, function, pretargeted=pretargeted) if actor is player.instance: player.delay(info['cast_time'], delegate, 'channel-spell') else: actor.behavior.behavior.queue_action(delegate, info['cast_time']) return 'success' else: return _invoke_ability_continuation(info, actor, target_override, function)
def great_dive_channel(actor, target): ui.message("{} {} high into the air!".format( syntax.name(actor).capitalize(), syntax.conjugate(actor is player.instance, ['rise', 'rises'])))