def wild_growth(actor, target, context): import mapgen terrain = main.current_map.tiles[target.x][target.y].tile_type if target.fighter and target.fighter.has_status('immobilized'): return 'cancelled' if terrain == 'grass floor': if target.fighter: if target is player.instance or fov.player_can_see( target.x, target.y): ui.message('The grass sprouts a tangle of grasping vines!', libtcod.lime) duration = main.roll_dice(context['root_duration']) immobilized = target.fighter.apply_status_effect( effects.immobilized(duration=duration), dc=context['save_dc']) if immobilized: target.fighter.apply_status_effect(effects.StatusEffect( 'wild growth', time_limit=duration, color=libtcod.lime, on_tick=wild_growth_tick, message='You are gripped by writhing vines!', description='This unit will take damage every turn', cleanseable=True), dc=None) else: if target is player.instance or fov.player_can_see(target.x, target.y): ui.message('Grass springs from the %s...' % terrain, libtcod.lime) grass = mapgen.create_terrain_patch((target.x, target.y), 'grass floor', min_patch=4, max_patch=12, overwrite=False) for tile in grass: main.changed_tiles.append(tile)
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 green_touch(actor, target, context): x, y = target import mapgen t = main.current_map.tiles[x][y] if not t.is_floor: if actor is player.instance: ui.message('You cannot grow grass here.', libtcod.gray) return 'cancelled' if actor is player.instance or fov.player_can_see(x, y): ui.message('Grass springs from the ground!', spells.essence_colors['life']) grass = mapgen.create_terrain_patch((x, y), 'grass floor', min_patch=4, max_patch=12) mapgen.scatter_reeds(grass, probability=30) for tile in grass: main.changed_tiles.append(tile) fov.set_fov_properties( tile[0], tile[1], len(main.get_objects(tile[0], tile[1], lambda o: o.blocks_sight)) > 0, elevation=main.current_map.tiles[tile[0]][tile[1]].elevation) return 'success'
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 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 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 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 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 firebomb(actor, target, context): (x, y) = context['origin'] ui.render_projectile((actor.x, actor.y), (x, y), spells.essence_colors['fire'], chr(7)) ui.render_explosion(x, y, 1, libtcod.yellow, libtcod.flame) if actor is player.instance or fov.player_can_see(x, y): ui.message('The firebomb explodes!', spells.essence_colors['fire']) for f in target: if combat.attack_magical( actor.fighter, f, 'ability_fire_bomb') == 'hit' and f.fighter is not None: f.fighter.apply_status_effect(effects.burning())
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 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 icebomb(actor, target, context): (x, y) = context['origin'] ui.render_projectile((actor.x, actor.y), (x, y), spells.essence_colors['cold'], chr(7)) ui.render_explosion(x, y, 1, libtcod.white, libtcod.light_sky) if actor is player.instance or fov.player_can_see(x, y): ui.message('The icebomb explodes!', spells.essence_colors['cold']) for f in target: if combat.attack_magical( actor.fighter, f, 'ability_ice_bomb') == 'hit' and f.fighter is not None: f.fighter.apply_status_effect( effects.frozen(duration=context['freeze_duration']))
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 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 timebomb(actor, target, context): (x, y) = target[0], target[1] rune = main.GameObject( x, y, chr(21), 'time bomb', spells.essence_colors['arcane'], description='"I prepared explosive runes this morning"') main.current_map.add_object(rune) rune_ticker = main.Ticker(context['delay'], _timebomb_ticker) rune_ticker.rune = rune rune_ticker.actor = actor main.current_map.tickers.append(rune_ticker) if actor is player.instance or fov.player_can_see(x, y): ui.message('A glowing rune forms...', spells.essence_colors['arcane'])
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 create_teleportal(actor, target, context): x, y = target portal = main.GameObject(x, y, 9, 'teleportal', spells.essence_colors['arcane'], on_tick=teleportal_on_tick) portal.timer = 4 main.current_map.add_object(portal) main.changed_tiles.append((x, y)) if fov.player_can_see(x, y): ui.message( 'A volatile portal opens. In a few moments it will teleport creatures standing near it.', spells.essence_colors['arcane']) 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 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 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 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_dragonweed(actor, target, context): x, y = target tile = main.current_map.tiles[x][y] if tile.tile_type != 'grass floor': if actor is player.instance: ui.message('The dragonseed must be planted on grass.', libtcod.gray) return 'cancelled' seed = main.GameObject( x, y, 'w', 'dragonweed sapling', libtcod.dark_chartreuse, description= 'A small, scaly blulb surrounded by sharp, thin leaves. In a few turns, ' 'it will grow into a full-sized Dragonweed.') main.current_map.add_object(seed) seed_ticker = main.Ticker(4, _dragonseed_ticker) seed_ticker.seed = seed main.current_map.tickers.append(seed_ticker) if actor is player.instance or fov.player_can_see(x, y): ui.message('A dragonseed is planted...', libtcod.dark_chartreuse) return 'success'
def summon_roaches(actor, attacker, damage): if not hasattr(actor, 'summon_limit') or not hasattr(actor, 'summons'): actor.summon_limit = 8 actor.summons = [] remove = [] for s in actor.summons: if s.fighter is None or not s in main.current_map.fighters: remove.append(s) for s in remove: actor.summons.remove(s) if len(actor.summons) >= actor.summon_limit: return if fov.player_can_see(actor.x, actor.y): ui.message( 'Cockroaches crawl from %s wounds!' % syntax.name(actor, possesive=True), libtcod.dark_magenta) for adj in main.adjacent_tiles_diagonal(actor.x, actor.y): if len(actor.summons) >= actor.summon_limit: break if not main.is_blocked( adj[0], adj[1]) and libtcod.random_get_int(0, 1, 10) <= 5: actor.summons.append( main.spawn_monster('monster_cockroach', adj[0], adj[1]))