def command_ask_leave_loc(game): """ Function that prompts player before actually leaving location :param game: a Game object """ director = game_view.GameLoop.active_director flee = False text = _('Do you really want to leave this location?') for point in game_logic.circle_points( r=25, include_center=False): # check for an enemy near player x = point[0] + game.player.position[0] y = point[1] + game.player.position[1] if game.current_loc.is_in_boundaries(x, y): if game.current_loc.cells[x][y].is_there_a(game_logic.Fighter): flee = True # if there are an enemy - set flee flag on text += _( '\n[color=red]WARNING: There are enemies nearby! If you choose to leave now, you will be chased, and probably lose some items while fleeing.' ) break director.push_scene( game_view.MultiButtonMessageScene(buttons=[ (_('Yes, head back to the camp.'), text, lambda g=game, f=flee: command_leave_loc(game=g, flee=f)), (_('No, stay here.'), text, None) ], title=_('Leaving location'), layout_options='intrinsic'))
def act_apply_timed_effect(action, register_call, target, effect, message_color): """ Applies an effect to target for ammount of time """ if register_call: # part executed when function is registered in ActionMgr target.effects.append(effect) # apply effect else: # part that is executed when action fires if target and effect in target.effects: # if target still exists and effect too if isinstance(target, game_logic.Player): # if Player was a target - inform about effect end game_logic.Game.add_message(message= _('{eff_name} effect fades away.').format( eff_name=_(effect.eff).replace('_', ' ').capitalize()), level='PLAYER', color=message_color) target.effects.remove(effect) # remove effect
def command_player_dead(game): """ Command that is excutes if player dies """ game_logic.Game.log.clear() director = game_view.GameLoop.active_director director.game = None del game director.push_scene( game_view.SingleButtonMessageScene( message=_('Horrors of the Desert City got you.'), title=_('You are dead.'), button_text=_('Return to main menu'), callback=lambda: command_return_to_main_menu(), layout_options='intrinsic'))
def react_remove_effect(self, reaction, event_data): """ Reaction, that removes effects """ reaction_result = {'success': False, 'removed_effects_count': 0} # result dict # if there will be different reaction targets - specify here target = self.owner # default if reaction['effects_number'] == 'all': # if all effects has to be removed r = 0 # number of removed effects for effect in target.effects[:]: # remove all such effects if effect.eff == reaction['effect'].eff: target.effects.remove(effect) r += 1 if r > 0: # if at least one effect removed - reaction successiful reaction_result['success'] = True reaction_result['removed_effects_count'] = r if isinstance(target, game_logic.Player): # if player uses - inform him of effect game_logic.Game.add_message(message= _('{ability_name}: removed all {eff_name} effects.').format( ability_name=_(self.name).capitalize(), eff_name=_(reaction['effect'].eff).upper()), level='PLAYER', color=self.message_color) else: # if specific number of effects has to be removed # choose needed effects needed_effects = [e for e in target.effects if e.eff == reaction['effect'].eff] if len(needed_effects) > 0: # if there are r = 0 # number of removed effects # if effects to be removed number less than present effects if len(needed_effects) < reaction['effects_number']: # remove random effects for effect in random.sample(needed_effects, reaction['effects_number']): target.effects.remove(effect) r += 1 if isinstance(target, game_logic.Player): # if player uses - inform him of effect game_logic.Game.add_message(message= _('{ability_name}: removed {eff_number} {eff_name} effects.').format( ability_name=_(self.name).capitalize(), eff_number=str(len(needed_effects)), eff_name=_(reaction['effect'].eff).upper()), level='PLAYER', color=self.message_color) else: # if not for effect in target.effects[:]: # remove all such effects if effect.eff == reaction['effect'].eff: target.effects.remove(effect) r += 1 if isinstance(target, game_logic.Player): # if player uses - inform him of effect game_logic.Game.add_message(message= _('{ability_name}: removed all {eff_name} effects.').format( ability_name=_(self.name).capitalize(), eff_name=_(reaction['effect'].eff).upper()), level='PLAYER', color=self.message_color) if r > 0: # if at least one effect removed - reaction successiful reaction_result['success'] = True reaction_result['removed_effects_count'] = r return reaction_result
def react_deal_damage(self, reaction, event_data): """ Reaction, that deals damage """ reaction_result = {'success': False, 'damage': 0, 'target': None} # reaction result dict if 'strike_type' in reaction: # determine strike type strike_type = reaction['strike_type'] else: strike_type = 'default' strike = game_logic.Strike(strike_type=strike_type, damage=reaction['damage'], dmg_type=reaction['dmg_type']) if reaction['target'] == 'projectile_hit_entity': # if target is hit by projectile target = event_data['target'] attacker = event_data['attacker'] elif reaction['target'] == 'mover': # if target is mover target = event_data['entity'] attacker = self.owner else: # default target and attacker target = event_data['target'] attacker = self.owner if isinstance(target, game_logic.BattleEntity): # if target is a damageable BE damage_dealt = attacker.land_strike(strike=strike, target=target) # land strike game_logic.Game.add_message(message= _('{ability_name}: {target_name} takes {damage} {dmg_type} damage!').format(ability_name=_(self.name), target_name=str(target), damage=str(damage_dealt), dmg_type= _(reaction['dmg_type'])), level='PLAYER', color=self.message_color) reaction_result['damage'] = damage_dealt reaction_result['target'] = target if damage_dealt > 0: reaction_result['success'] = True else: # tried to deal damage to not BattleEntity game_logic.Game.add_message( self.name + ': attempted to deal damage to not BE.', 'DEBUG', self.message_color) return reaction_result
def description(self): kwargs = self.properties if self.eff in eff_descriptions: descr = eff_descriptions[self.eff] else: descr = self.eff return _(descr).format(magn=str(self.magnitude), **kwargs)
def act_pick_up_item(action, register_call, actor, item): """ Actor pick up item action """ if register_call: # part executed when function is registered in ActionMgr action.t_needed = actor.speed / 4 # pick up action is 1/4 speed else: # part that is executed when action fires if isinstance(actor, game_logic.Player): msg = _('You pick up {item}.').format(item=str(item)) game_logic.Game.add_message(message=msg, level='PLAYER', color=[255, 255, 255]) actor.add_item(item) # pick up item actor.actions.remove(action) # remove performed action from actor's list actor.state = 'ready' # return actor to ready state
def react_deal_periodic_damage(self, reaction, event_data): """ Reaction, that applies periodic damage """ # if there will be different reaction targets - specify here target = event_data['target'] # default target and attacker attacker = event_data['attacker'] self.owner.location.action_mgr.register_action(1, actions.act_deal_periodic_damage, self.owner.location.action_mgr, target, pickle.loads(pickle.dumps(reaction['effect'])), reaction['damage'], reaction['dmg_type'], reaction['period'], reaction['whole_time'], self.message_color, reaction['stackable']) # doing pickle copy of effect to make every stack separate if isinstance(attacker, game_logic.Player): # if player applies - inform him of effect game_logic.Game.add_message(message= _('{target_name} is {eff_name}.').format( target_name=str(target).capitalize(), eff_name=_(reaction['effect'].eff).lower()), level='PLAYER', color=self.message_color) return {'success': True} # PLACEHOLDER applying periodic damage is always successiful now
def command_reload(game, item): """ Command function for player wants to reload specific ranged weapon """ director = game_view.GameLoop.active_director if isinstance( item, game_logic.ItemRangedWeapon): # check if there is ranged weapon if len(item.ammo) < item.ammo_max: # check if it is loaded # select appropriate ammo items ammos = [ a for a in game.player.inventory if item.ammo_type in a.categories ] if ammos: if len(ammos) == 1: game.player.perform(actions.act_reload, game.player, item, ammos[0]) game.start_update_thread() else: director.push_scene( game_view.AmmoItemSelectionScene( items=ammos, game=game, ranged_weapon=item, caption=_('Load ammo:'), layout_options=LayoutOptions(top=0.30, bottom=0.30, left=0.30, right=0.30))) else: game_logic.Game.add_message( message=_('No {ammo_type} type ammunition.').format( ammo_type=_(item.ammo_type)).capitalize(), level='PLAYER', color=[255, 255, 255]) else: game_logic.Game.add_message( message=_('{item} is fully loaded.').format( item=str(item)).capitalize(), level='PLAYER', color=[255, 255, 255])
def react_apply_timed_effect(self, reaction, event_data): """ Reaction, that applies a timed effect """ # if there will be different reaction targets - specify here target = self.owner # default if reaction['target'] == 'projectile_hit_entity': target = event_data['target'] if isinstance(event_data['attacker'], game_logic.Player): # if player attacks - inform him of effect game_logic.Game.add_message(message=_('{ent_name} is now {eff_name}: {eff_descr} for {time} ticks.'). format(ent_name=str(target), eff_name= _(reaction['effect'].eff). capitalize().replace('_', ' '), eff_descr=reaction['effect'].description, time=str(reaction['time'])), level='PLAYER', color=self.message_color) self.owner.location.action_mgr.register_action(reaction['time'], actions.act_apply_timed_effect, target, reaction['effect'], self.message_color) if isinstance(target, game_logic.Player): # if player uses - inform him of effect game_logic.Game.add_message(message=_('{eff_name}: {eff_descr} for {time} ticks.').format(eff_name= _(reaction['effect'].eff). capitalize().replace('_', ' '), eff_descr=reaction['effect'].description, time=str(reaction['time'])), level='PLAYER', color=self.message_color) if reaction['target'] == 'fired_ability_target': target = event_data['target'] attacker = event_data['owner'] try: attacker = event_data['owner'].launcher except AttributeError: pass if isinstance(attacker, game_logic.Player): # if player attacks - inform him of effect game_logic.Game.add_message(message=_('{ent_name} is now {eff_name}: {eff_descr} for {time} ticks.'). format(ent_name=str(target), eff_name= _(reaction['effect'].eff). capitalize().replace('_', ' '), eff_descr=reaction['effect'].description, time=str(reaction['time'])), level='PLAYER', color=self.message_color) self.owner.location.action_mgr.register_action(reaction['time'], actions.act_apply_timed_effect, target, reaction['effect'], self.message_color) if isinstance(target, game_logic.Player): # if player uses - inform him of effect game_logic.Game.add_message(message=_('{eff_name}: {eff_descr} for {time} ticks.').format(eff_name= _(reaction[ 'effect'].eff). capitalize().replace( '_', ' '), eff_descr= reaction[ 'effect'].description, time=str(reaction[ 'time'])), level='PLAYER', color=self.message_color) return {'success': True} # PLACEHOLDER effect applying is always successiful now
def command_throw_choose(game, main_scene): """ Command function for player wants to throw an item in hands - choose target """ right = game.player.equipment['RIGHT_HAND'] left = game.player.equipment['LEFT_HAND'] items = [i for i in [right, left] if i is not None] # pick equipment in hands if items: # check if there are any if len(items) == 1: # if one if game.player.get_throw_range(items[0]) > 0: main_scene.start_targeting( range=game.player.get_throw_range(items[0]), t_object=items[0], eligible_types=(game_logic.BattleEntity, 'point'), callback=command_throw, player=game.player, item=items[0]) else: game_logic.Game.add_message(message=_( '{item} is too heavy!'.format( item=str(items[0])).capitalize()), level='PLAYER', color=[255, 255, 255]) else: # if multiple items in hands main_scene.director.push_scene( game_view.ThrowItemSelectionScene(items=items, game=game, caption=_('Throw item:'), layout_options=LayoutOptions( top=0.30, bottom=0.30, left=0.30, right=0.30))) else: game_logic.Game.add_message( message=_('Take something in hand to throw it.'), level='PLAYER', color=[255, 255, 255])
def command_fire_choose(game): """ Command function for player wants to fire ranged weapon - choose target """ director = game_view.GameLoop.active_director ranged_weapons = [ w for w in list(game.player.equipment.values()) if isinstance(w, game_logic.ItemRangedWeapon) ] # pick ranged weapons in equipment if ranged_weapons: # check if there are any if len(ranged_weapons) == 1: # if one if len(ranged_weapons[0].ammo) > 0: # if it has ammo loaded director.main_game_scene.start_targeting( range=ranged_weapons[0].range, t_object=ranged_weapons[0], eligible_types=(game_logic.BattleEntity, 'point'), callback=command_fire, player=game.player, weapon=ranged_weapons[0]) else: game_logic.Game.add_message( message=_("{weapon} isn't loaded!").format( weapon=str(ranged_weapons[0]).capitalize()), level='PLAYER', color=[255, 255, 255]) else: # if multiple ranged equipped director.push_scene( game_view.FireItemSelectionScene(items=ranged_weapons, game=game, caption=_('Fire weapon:'), layout_options=LayoutOptions( top=0.30, bottom=0.30, left=0.30, right=0.30))) else: game_logic.Game.add_message(message=_('Equip ranged weapon to fire.'), level='PLAYER', color=[255, 255, 255])
def act_launch_projectile(action, register_call, projectile_type, launcher, target, message_color): """ Launches a projectile """ if register_call: # part executed when function is registered in ActionMgr pass # nothing to do on action registration else: # part that is executed when action fires if launcher and projectile_type: # if all participants still exist # create new projectile copy projectile = dataset.get_entity(projectile_type, {'launcher': launcher, 'target': target}) projectile.ai.owner = projectile # set projectile ai component owner projectile.ai.target = target launcher.location.reg_entity(projectile) # register projectile to location projectile.launch(launcher.position[0], launcher.position[1]) game_logic.Game.add_message(message= _('{launcher_name} launches a {projectile_name}!').format( launcher_name=str(launcher), projectile_name=str(projectile)), level='PLAYER', color=message_color)
def command_execute_debug_line(line, game): """ Executes single debug command line """ # unsafe, but will do for now director = game_view.GameLoop.active_director player = game.player loc = game.current_loc if game.player.position: x = game.player.position[0] y = game.player.position[1] try: exec(line, globals(), locals()) except: game_logic.Game.add_message( message=_('Failed to execute line: {line}').format( line=line).capitalize(), level='DEBUG', color=[255, 0, 0]) e = sys.exc_info()[0] print('WARNING! Failed to execute debug line: ' + line + '\n' + str(e))
def command_pick_up(game, dx, dy): """ Command function for player wants to pick up some items """ director = game_view.GameLoop.active_director player = game.player loc = game.current_loc x = player.position[0] + dx y = player.position[1] + dy if loc.is_in_boundaries( x, y): # check if position of selected cell is in boundaries items = [ i for i in loc.cells[x][y].entities if isinstance(i, game_logic.Item) ] # select items in cell if items: # check if there is an item if len(items) == 1: if isinstance(items[0], game_logic.ItemCharges) and\ 'stackable' in items[0].categories and items[0].charges > 1: director.push_scene( game_view.NumberInputScene( num_range=(1, items[0].charges), num_start=items[0].charges, title=str(items[0]), callback=lambda text, item=items[0], game=game: _split_stack_and_pick(text, item, game))) else: player.perform(actions.act_pick_up_item, player, items[0]) else: # if there are multiple Items - ask which to pick up? director.push_scene( game_view.PickUpItemSelectionScene( items=items, game=game, caption=_('Pick up item:'), layout_options=LayoutOptions(top=0.25, bottom=0.25, left=0.2, right=0.2)))
def act_deal_periodic_damage(action, register_call, act_mgr, target, effect, damage, dmg_type, period, whole_time, message_color, stackable=False): """ Periodically damages target while effect is on target """ if register_call: # part executed when function is registered in ActionMgr if stackable: # if effect is stackable if effect in target.effects: # if there is particularly this effect in pass # do nothing, deal damage further else: act_mgr.register_action(whole_time, act_apply_timed_effect, target, effect, message_color) # apply a new timed effect if isinstance(target, game_logic.Player): # if Player was a target - inform about effect game_logic.Game.add_message(message= _('{eff_name}!').format(eff_name=_(effect.eff)).capitalize(), level='PLAYER', color=message_color) else: # if not stackable if effect in target.effects: # if there is particularly this effect in pass # do nothing, do damage further else: # if no this effect, check for similar effects if target.get_effect(effect.eff) > 0: # if there are such effect type act_mgr.remove_action(action) # stop action return else: act_mgr.register_action(whole_time, act_apply_timed_effect, target, effect, message_color) # apply a new timed effect if isinstance(target, game_logic.Player): # if Player was a target - inform about effect game_logic.Game.add_message(message= _('{eff_name}!').format(eff_name=_(effect.eff)).capitalize(), level='PLAYER', color=message_color) action.t_needed = period # set needed time to 1 period of damage else: # part that is executed when action fires if target and not target.dead: # if target still exists if effect in target.effects: # if effect still on target strike = game_logic.Strike(strike_type='periodic', damage=damage, dmg_type=dmg_type) damage_dealt = target.take_strike(strike=strike) # strike if isinstance(target, game_logic.Player): # if Player was a target - inform about effect game_logic.Game.add_message(message= _('{eff_name}: {damage} damage.').format( eff_name=_(effect.eff), damage=str(damage_dealt)).capitalize(), level='PLAYER', color=message_color) act_mgr.register_action(whole_time, act_deal_periodic_damage, act_mgr, target, effect, damage, dmg_type, period, whole_time, message_color, stackable) # apply next tick of damage
def command_leave_loc(game, flee=False): """ Function to execute when player wants to leave location :param game: a Game object :param flee: flee flag - if fleeing, some penalties take place """ director = game_view.GameLoop.active_director old_loc = game.current_loc player = game.player raid_report_text = '' # fleeing report section if flee: # check if player is fleeing from enemies lost_items = [] for item in player.inventory: if game_logic.weighted_choice([(True, 50), (False, 50) ]): # lose some inventory items lost_items.append(item) player.discard_item(item=item) for item in [sl for sl in list(player.equipment.values()) if sl]: # lose some equipped items if game_logic.weighted_choice([(True, 25), (False, 75)]): lost_items.append(item) player.unequip_item(item=item) player.discard_item(item=item) if len(lost_items) > 0: raid_report_text += _( 'You fled from the enemies, but lost some items in the process:\n' ) for item in lost_items: raid_report_text += _('{item}, ').format(item=str(item)) del item raid_report_text = raid_report_text[:-2] + '.\n' # a hack, replace last , with .\n else: raid_report_text += _( 'You fled from the enemies and kept all of your items. Lucky!\n' ) # treasures report section treasures = {} for item in player.inventory: if 'relic' in item.categories: # if there'll be other types of treasure - add here if isinstance(item, game_logic.ItemCharges): count = item.charges else: count = 1 if item.name in treasures: treasures[str(item)][0] += count else: treasures[str(item)] = [count, item.properties['value']] if len(treasures) > 0: raid_report_text += _('You obtained some treasures:\n\n') total = 0 for tr in treasures.keys(): raid_report_text += _( '{tr_name} x{tr_count} * {tr_value} = {tr_total}\n').format( tr_name=tr, tr_count=str(treasures[tr][0]), tr_value=str(treasures[tr][1]), tr_total=str(treasures[tr][0] * treasures[tr][1])) total += treasures[tr][0] * treasures[tr][1] raid_report_text += _('\nTotal treasures value: {total} coins.\n ' ).format(total=str(total)) else: raid_report_text += _( 'You escaped the City with nothing of value in your pockets. At least you managed to stay alive.\n' ) # actual leaving location section player.location.remove_entity(player) player.effects.clear() # simply clear list for now. Needs testing game.remove_location(old_loc) game.enter_camp() game.equipment_merchant.restock() director.pop_scene() director.push_scene( game_view.SingleButtonMessageScene( message=raid_report_text + '\n \n ', title=_('Successful raid.'), layout_options='intrinsic', close_on_esc=False, callback=lambda: (director.pop_scene( ), director.push_scene(game_view.CampMenuScene(game=game)))))