def affect(self, source, effect, params): ''' What happens from an effect from a spell or potion ''' result = False if effect == Effects.Damage: result = self.damage(params) if result: self.notify_observers_reply( ReplyHelpers.render_action_template( 'destroyed', source_text=source.description, item_name=self.description)) elif effect == Effects.Repair: result = self.repair(params) if result: self.notify_observers_reply( ReplyHelpers.render_action_template( 'repaired', source_text=source.description, item_name=self.description)) else: self.notify_observers_reply( ReplyHelpers.render_action_template( 'no_effect', item_text=self.name, effect_item=source.description)) return result
def sell(self, player, item_text, amount=1): item = player.get_item_by_name(item_text) if item is None: return ReplyHelpers.render_action_template('not_carrying_unknown', action=Actions.get_action_text(Actions.SELL)) item = item.one_of() item.add(amount-1) if not self.can_sell(item): return ReplyHelpers.render_action_template('sell_no_item', item_prefix=item.text_prefix, item_text=item.description) #Has item to sell itm = player.inventory.get_item(item) if itm is None: return ReplyHelpers.render_action_template('not_carrying', action=Actions.get_action_text(Actions.SELL), item_prefix=item.text_prefix, item_text=item.description) if itm.count < item.count: return ReplyHelpers.render_action_template('not_carrying_enough', action=Actions.get_action_text(Actions.SELL), item_text=item.description_plural) description = item.description if item.count > 1: description = item.description_plural #Sell items player.drop(item) player.inventory.add_money(item.cost) self.__add_item(item) return ReplyHelpers.render_action_template('sell_item', item_text=description)
def throw(self, item, target): ''' Returns True if hit, False if miss or can't throw TODO: May need a better return ''' if not self.is_carrying(item): #You're not carrying {{item_prefix}} {{item_text}} to {{action}}. self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_carrying', item_prefix=item.text_prefix, item_text=item.name, action='throw')) return False if not isinstance(item, weapons.Weapon) or not item.is_throwable: self.notify_observers_log("{} - weapon {} is not throwable".format( self.class_name, item.name)) self.notify_observers_reply( ReplyHelpers.render_action_template('not_throwable', weapon_text=item.name)) return False result = item.throw(self, target) #Maybe give if hit? self.drop(item.one_of()) return result
def drink(self, item): if not isinstance(item, potions.Potion) and not isinstance( item, items.Drink): self.notify_observers_reply( ReplyHelpers.render_action_template( 'drink_cannot', item_prefix=item.text_prefix, item_text=item.description)) return False if not self.is_carrying(item): self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_carrying', action=Actions.get_action_text(Actions.DRINK), item_prefix=item.text_prefix, item_text=item.description)) return False self.notify_observers_reply( ReplyHelpers.render_action_template(self.get_reply_key('drink'), item_prefix=item.text_prefix, item_text=item.description, attacker_name=self.name)) if item.drink(self): self.get_rid_of_one(item) return True return False
def buy(self, player, item_text, amount=1): #Store has item to sell item = None for itm in self.__items: if itm.is_match(item_text): item = itm.one_of() break if item == None: return ReplyHelpers.render_action_template('buy_no_item') item.add(amount-1) description = item.description if item.count > 1: description = item.description_plural #Can afford items if player.money < item.cost: return ReplyHelpers.render_action_template('buy_no_money', item_text=description) #Buy items player.inventory.remove_money(item.cost) self.__remove_item(item) player.pickup(item) return ReplyHelpers.render_action_template('buy_item', item_text=description)
def _effect(self, caster, target): dmg = self._damage(caster) if dmg == 0: caster.notify_observers_reply(ReplyHelpers.render_action_template(caster.get_reply_key('cast_defend'), spell_text=self.description, attacker_name=caster.name, defender_name=target.name, damage=dmg, hit_points=target.hit_points, mana_points=caster.mana_points)) else: caster.notify_observers_log("{} {} hit! Damage {}".format(caster.class_name, self.class_name, dmg)) caster.notify_observers_reply(ReplyHelpers.render_action_template(caster.get_reply_key('cast_hit'), spell_text=self.description, attacker_name=caster.name, defender_name=target.name, damage=dmg, hit_points=target.hit_points, mana_points=caster.mana_points)) target.affect(self, Effects.Damage, dmg)
def count_money(self, player): carried_gold = player.inventory.get_item(items.Gold()) carried_silver = player.inventory.get_item(items.Silver()) carried_copper = player.inventory.get_item(items.Copper()) if carried_gold + carried_silver + carried_copper > 0: result = ReplyHelpers.render_action_template('money_have') else: result = ReplyHelpers.render_action_template('money_none') if carried_gold > 0: result += ' ' + ReplyHelpers.render_action_template('money_count', type='gold', amount=carried_gold) if carried_silver > 0: result += ReplyHelpers.render_action_template('money_count', type='silver', amount=carried_silver) if carried_copper > 0: result += ReplyHelpers.render_action_template('money_count', type='copper', amount=carried_copper) return result
def search(self, player, item=None, item_text=None): #Search floor if isinstance(item, Floor): return self.__search_floor(player) #Search room or walls if (item_text is None or item_text in ['room','area']) or isinstance(item, Walls): self._set_state('room_searched', True) self.on_search_room() floor_items = '' if len(self._floor_items) > 0: floor_text = self.__get_room_text()[1] floor_items = ReplyHelpers.render_room_template('items_on_floor',floor_text=floor_text) return ReplyHelpers.render_room_template('{}_search'.format(self.name.lower())) + ' ' + floor_items + ' ' + self.__check_for_bodies() #Search bodies if isinstance(item, players.Player): monster = item if monster.is_looted: return ReplyHelpers.render_action_template('monster_looted',monster_name=monster.class_name) found_items = player.loot_body(monster) result = ReplyHelpers.render_room_template('search_item',item=monster.class_name) result += ' ' + self.__list_items_reply(found_items, pickup=False) #Search items elif isinstance(item, SearchableItem): found_items = item.search() result = ReplyHelpers.render_room_template('search_item',item=item.description) result += ' ' + self.__list_items_reply(found_items, pickup=True, player=player) else: return ReplyHelpers.render_room_template('item_not_searchable') self.on_search_item(item, found_items) return result
def test_throw_actions(self): gm = game_manager player = gm.create_player('thief') gm.start_new_game('fred', player) user = gm.get_user('fred', 'test') user.set_room(rooms.CellRoom(player)) room = user.room dagger = weapons.Dagger() #Throw item you don't have reply = room.throw(dagger, None) self.assertTrue('not carrying' in reply) print(reply) #Throw at non-existent item player.pickup(dagger) reply = room.throw(dagger, 'chest') self.assertTrue( ReplyHelpers.render_action_template( 'no_such_target', item_text=dagger.description) in reply) print(reply) #Throw item at door reply = room.throw(dagger, 'door') self.assertTrue('door' in reply) print(reply) self.assertTrue('you throw' in reply.lower())
def __build_reply(self, ob): result = '' for reply in ob.replies: if Helpers.is_string(reply): result += ' ' + reply continue args = reply['args'] key = reply['key'] item = None if 'item' in args.keys(): item_name = args['item'] item = self.get_room_item_by_name(item_name) template = args['template'] if 'attacker_name' in args.keys(): if args['attacker_name'].lower() in ['thief','ranger','fighter','mage']: key = 'player_'+key else: key = 'monster_'+key if template == ReplyHelpers.TEMPLATE_ACTION: result += ' ' + ReplyHelpers.render_action_template(key, **args) elif template == ReplyHelpers.TEMPLATE_ROOM: if item: result += ' ' + ReplyHelpers.try_render_room_template(self, item, key, **args) else: result += ' ' + ReplyHelpers.render_room_template(key, **args) elif template == ReplyHelpers.TEMPLATE_COMMON: result += ' ' + ReplyHelpers.render_common_template(key, **args) elif template == ReplyHelpers.TEMPLATE_DESCRIPTION: result += ' ' + ReplyHelpers.render_descr_template(key, **args) return result
def cast(self, player, spell_text, target): spell = spells.SpellStats.get_spell_by_text(spell_text) if not spell: return ReplyHelpers.render_action_template('spell_cannot') with Observer(player) as ob: if not player.can_cast_spell(spell): return ReplyHelpers.render_action_template('spell_cannot_cast',spell_name=spell.description) + self.__build_reply(ob) result = '' player.cast(spell, target) #You attacked a poor monster if isinstance(target, players.Monster): target.strike(player) result += ' ' + self.__build_reply(ob) return result
def test__ALEXA_LOCAL_character_reponse(self): request = self.test__ALEXA_LOCAL_start_game() request.set_intent('CharacterIntent') request.set_slots([request.create_slot('name', 'mage')]) response = request.post(request.session_id) print(response.data) self.assertTrue( ReplyHelpers.render_action_template('char_choice', char='mage') in response.get_output_text())
def drink(self, player): if not player.is_carrying(self): self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_carrying', item_prefix=self.text_prefix, item_text=self.description, action='drink')) return False if self.count == 0: self.notify_observers_reply( ReplyHelpers.render_action_template('nothing_to_drink', item_text=self.name)) return False player.notify_observers_log("{} - drank {}".format( player.class_name, self.class_name)) self.count -= 1 self._effect('drink', player) return True
def strike(self, attacker, defender, is_throw=False): if self.ammo_count(attacker) == 0: self.notify_observers_log("{} - weapon {} has no ammo".format( attacker.class_name, self.class_name)) self.notify_observers_reply( ReplyHelpers.render_action_template( 'no_ammo', ammo_text=self.ammo_class, weapon_text=self.description)) return super(RangedWeapon, self).strike(attacker, defender, is_throw)
def __check_for_monster(self, player): #Has a monster appeared? monster = self.get_next_monster() result = '' if monster is None: player._end_battle() else: #Yikes a monster! if not player.is_in_battle: player._start_battle() with Observer(player) as ob: if GameRules.roll_initiative_check(monster) > GameRules.roll_initiative_check(player): #Monster strikes first result = ReplyHelpers.render_action_template('monster_detects_you',monster_name=monster.class_name) monster.strike(player) else: #Player gets to attack first result = ReplyHelpers.render_action_template('monster_detected',monster_name=monster.class_name) result += ' ' + self.__build_reply(ob) return result
def eat(self, item): if not isinstance(item, items.Food): ReplyHelpers.render_action_template('eat_cannot', item_text=item.description) return False if not self.is_carrying(item): self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_carrying', action=Actions.get_action_text(Actions.EAT), item_prefix=item.text_prefix, item_text=item.description)) return False self.notify_observers_reply( ReplyHelpers.render_action_template('eat_food', item_text=item.description)) if item.eat(self): self.get_rid_of_one(item) return True return False
def heal(self, points): if points < 1 or self.hit_points == self.max_hit_points or self.is_dead: return False self.__set_hit_points(self.hit_points + points) self.notify_observers_reply( ReplyHelpers.render_action_template( self.get_reply_key('healed'), points=min(points, self.max_hit_points - self.hit_points), max_hit_points=self.max_hit_points, hit_points=self.hit_points)) return True
def _hit(self, caster, target): defense_roll = GameRules.roll_saving_spell_throw(self, target) attack_roll = GameRules.roll_spell_attack_score(self, caster) caster.notify_observers_log("{} - cast {}".format(caster.class_name, self.class_name)) caster.notify_observers_log('Defense: {}({}), Attack: {}({})'.format(defense_roll.total,defense_roll.roll,attack_roll.total,attack_roll.roll)) hit = defense_roll.roll < 20 and attack_roll.total >= defense_roll.total if not hit: caster.notify_observers_log("{} - {} missed {}".format(caster.class_name, self.class_name, target.class_name)) caster.notify_observers_reply(ReplyHelpers.render_action_template(caster.get_reply_key('cast_miss'), spell_text=self.description, attacker_name=caster.name, defender_name=target.name, mana_points=caster.mana_points)) return hit
def throw(self, player, item, target_text): with Observer(player) as ob: if not player.is_carrying(item): player.notify_observers_reply('not_carrying', template=ReplyHelpers.TEMPLATE_ACTION, action=Actions.get_action_text(Actions.THROW), item_prefix=item.text_prefix, item_text=item.description) return self.__build_reply(ob) target = None if target_text is None: target = self.get_next_monster() else: for m in self.get_monsters(): if m.is_match(target_text): target = m break #Ok probably room item if not target: target = self.get_room_item_by_text(target_text) if not target: return ReplyHelpers.render_action_template('no_such_target', item_text=item.description) #Either throw at a room item or a monster is_monster = isinstance(target, players.Monster) is_player = isinstance(target, players.Player) and not is_monster is_weapon = isinstance(item, weapons.Weapon) is_item = isinstance(target, items.Item) or isinstance(target, RoomItem) floor_text = self.__get_room_text()[1] with Observer(player) as ob: is_hit = player.throw(item, target) if is_hit and is_item: player.notify_observers_reply('throw_item_hit', template=ReplyHelpers.TEMPLATE_ACTION, item_text=item.description, target_text=target.description, floor_text=floor_text) elif is_item: player.notify_observers_reply('throw_item_miss', template=ReplyHelpers.TEMPLATE_ACTION, item_text=item.description, target_text=target.description, floor_text=floor_text) if is_hit and is_monster and is_weapon: #Hit and lodged in monster if not GameRules.is_thrown_item_lost(): target.pickup(item) elif is_hit and is_player: #Hit and lodged in player pass else: #Miss or hit and on floor if not GameRules.is_thrown_item_lost(): self.add_floor_item(item.one_of()) if is_monster and is_weapon and not target.is_dead: target.strike(player) return self.__build_reply(ob)
def loot_body(self, body): result = [] if body.is_looted: self.notify_observers_reply( ReplyHelpers.render_action_template( 'already_looted', body_name=body.description)) return False for item in body.inventory.items(): body.__inventory.remove(item) self.__inventory.add(item) result.append(item) self.__is_looted = True return result
def can_cast_spell(self, spell): if not self.can_act: self.notify_observers_reply( ReplyHelpers.render_action_template( 'cannot_act', condition=self.current_condition)) return False if not self.knows_spell(spell): self.notify_observers_reply( ReplyHelpers.render_action_template( 'spell_not_proficient', spell_name=spell.description)) return False if spell.level > self.level: self.notify_observers_reply( ReplyHelpers.render_action_template( 'spell_too_advanced', spell_name=spell.description)) return False if not self.mana_points >= spell.level: self.notify_observers_reply( ReplyHelpers.render_action_template( 'spell_not_enough_mana', spell_name=spell.description)) return False return True
def learn_spell(self, spell): if not isinstance(spell, spells.Spell): self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_spell', spell_name=spell.description)) return False if self.level < spell.level: self.notify_observers_reply( ReplyHelpers.render_action_template( 'spell_too_hard', spell_name=spell.description)) return False if not self.knows_spell(spell): self.__learned_spells.append(spell) self.notify_observers_reply( ReplyHelpers.render_action_template( 'spell_learned', spell_name=spell.description)) return True else: self.notify_observers_reply( ReplyHelpers.render_action_template( 'spell_already_known', spell_name=spell.description)) return False
def wrapper(*args, **kwargs): inst = args[0] if not inst.can_act: inst.notify_observers_reply( ReplyHelpers.render_action_template( 'cannot_act', condition=inst.current_condition)) return False inst.current_action = action try: result = function(*args, **kwargs) finally: inst.current_action = False return result
def equip_armor(self, armr): if armr == None: raise AttributeError() if not self.is_carrying(armr): self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_carrying', item_prefix=armr.text_prefix, item_text=armr.description, action='equip')) return False if not isinstance(armr, armor.Armor): self.notify_observers_reply( ReplyHelpers.render_action_template( self.get_reply_key('equip_not_armor'), item_text=armr.description)) return False self.__armor = armr if not isinstance(armr, type(self.default_armor)): self.notify_observers_reply( ReplyHelpers.render_action_template( self.get_reply_key('equip_armor'), attacker_name=self.name, armor_text=armr.description)) return True
def equip_weapon(self, weapon): if weapon == None: raise AttributeError() if not self.is_carrying(weapon): self.notify_observers_reply( ReplyHelpers.render_action_template( 'not_carrying', item_prefix=weapon.text_prefix, item_text=weapon.description, action='equip')) return False if not isinstance(weapon, weapons.Weapon): self.notify_observers_reply( ReplyHelpers.render_action_template( self.get_reply_key('equip_not_weapon'), item_text=weapon.description)) return False self.__weapon = weapon if not isinstance(weapon, type(self.default_weapon)): self.notify_observers_reply( ReplyHelpers.render_action_template( self.get_reply_key('equip_weapon'), attacker_name=self.name, weapon_text=weapon.description)) return True
def strike(self, defender): defender_name = defender.__class__.__name__ attacker_name = self.__class__.__name__ if not self.can_act: self.notify_observers_reply( ReplyHelpers.render_action_template( self.get_reply_key('stunned'), defender_name=defender_name, attacker_name=attacker_name)) self.notify_observers_log( "{} - stunned, unconscious or dead - can't attack".format( attacker_name)) return False return self.get_equipped_weapon().strike(self, defender)
def strike(self, attacker, defender, is_throw=False): ''' Return Strike object Representing hiut or miss and damage (and critical of not) ''' player = attacker if attacker.is_monster: player = defender if not self.is_throwable and is_throw: player.notify_observers_reply( ReplyHelpers.render_action_template( 'not_throwable', weapon_text=self.description)) return False defender_name = defender.__class__.__name__ attacker_name = attacker.__class__.__name__ defender_ac = defender.get_defense() attacker.notify_observers_log("{} - defense {}".format( defender_name, defender_ac)) if is_throw: dice_roll = GameRules.roll_weapon_throw_score(attacker) else: dice_roll = GameRules.roll_weapon_attack_score(attacker) is_critical_strike = self.is_critical_strike(dice_roll.roll) is_critical_miss = self.is_critical_miss(dice_roll.roll) attacker.notify_observers_log("{} - attack roll: {}({})".format( attacker_name, dice_roll.total, dice_roll.roll)) strike_type = 'miss' if is_critical_miss or dice_roll.total < defender_ac: #Miss! if is_critical_miss: strike_type = 'critical_miss' if is_throw: strike_type = 'throw_' + strike_type player.notify_observers_reply( ReplyHelpers.render_action_template( attacker.get_reply_key(strike_type), attacker_name=attacker_name, defender_name=defender_name, attack_type=self.get_attack_type())) self._miss(attacker, defender) return Strike(False, is_critical_miss) #Hit dmg = 0 strike_type = 'strike' if is_critical_strike: dmg = self._critical_strike(attacker) strike_type = 'critical_strike' else: dmg = self._normal_strike(attacker) if is_throw: strike_type = 'throw_' + strike_type attacker.notify_observers_log("{} - damage roll: {}".format( attacker_name, dmg)) defender.affect(attacker, Effects.Damage, dmg) player.notify_observers_reply( ReplyHelpers.render_action_template( player.get_reply_key(strike_type), attacker_name=attacker_name, defender_name=defender_name, damage=dmg, hit_points=defender.hit_points, attack_type=self.get_attack_type())) attacker.notify_observers_log( "{} - DAMAGED {} {}, {} HP now {}!".format(attacker_name, defender_name, dmg, defender_name, defender.hit_points)) if defender.is_dead: player.notify_observers_reply( ReplyHelpers.render_action_template( player.get_reply_key('killed'), attacker_name=attacker_name, defender_name=defender_name)) return Strike(True, is_critical_strike, dmg)
def do_action(self, player, action, item_text, spell_text=None, target_text=None, amount=0): if player.is_in_battle and action not in Actions.ALLOWED_BATTLE_ACTIONS: reply_text = ReplyHelpers.render_action_template('battle_invalid_action', monster=self.get_next_monster().description) return QuestGameNormalReply(self, reply_text) item = None if not action in Actions.NO_ITEM_ACTIONS or item_text is not None: #Try and find a matching item if not item_text is None: if player.is_in_battle and action in Actions.ATTACK_ACTIONS: #Look for matching monster for m in self.get_monsters(): if m.is_match(item_text): item = m break if item is None: reply_text = ReplyHelpers.render_action_template('no_monster_to_battle') return QuestGameNormalReply(self, reply_text) elif action == Actions.DESCRIBE: #Could be in room or on player item = self.get_room_item_by_text(item_text) if item is None: item = player.get_item_by_name(item_text) if item is None: #Look for matching monster for m in self.get_monsters(): if m.is_match(item_text): item = m break if item is None: room_text = self.__get_room_text()[0] reply_text = ReplyHelpers.render_room_template('no_such_room_item', room=room_text, action=Actions.get_action_text(action)) return QuestGameNormalReply(self, reply_text) elif action in Actions.PLAYER_ITEM_ACTIONS: #Try items held by player item = player.get_item_by_name(item_text) if item is None: reply_text = ReplyHelpers.render_action_template('not_carrying_unknown', action=Actions.get_action_text(action)) return QuestGameNormalReply(self, reply_text) else: #Item is in the room item = self.get_room_item_by_text(item_text) if item is None and not action in Actions.NO_ITEM_ACTIONS: room_text = self.__get_room_text()[0] reply_text = ReplyHelpers.render_room_template('no_such_room_item', room=room_text, action=Actions.get_action_text(action)) return QuestGameNormalReply(self, reply_text) if item is None: if player.is_in_battle and action in Actions.ATTACK_ACTIONS: item = self.get_next_monster() if item is None: reply_text = ReplyHelpers.render_action_template('no_monster_to_battle') return QuestGameNormalReply(self, reply_text) elif self.__is_openable_action(action, spell_text): #Openable item openable_items = [] for itm in self._room_items: if not isinstance(itm, RoomOpenableItem): continue if (self.__is_lock_action(action, spell_text) and not itm.is_locked) or \ (self.__is_unlock_action(action, spell_text) and itm.is_locked) or \ (self.__is_open_action(action, spell_text) and not itm.is_open) or \ (self.__is_close_action(action, spell_text) and itm.is_open): openable_items.append(itm) if len(openable_items) == 1: item = openable_items[0] else: reply_text = ReplyHelpers.render_room_template('select_openable_item', action=Actions.get_action_text(action), item_list=ReplyHelpers.build_list(openable_items, 'description')) return QuestGameReply(reply_text, query_state=ReplyHelpers.QUERY_SELECT) if item is None: #Matching failed reply_text = ReplyHelpers.render_room_template('no_item_provided', action=Actions.get_action_text(action)) return QuestGameNormalReply(self, reply_text) #No item found reply_text = ReplyHelpers.render_action_template('not_carrying', action=Actions.get_action_text(action), item_prefix=item.text_prefix, item_text=item.description) return QuestGameNormalReply(self, reply_text) action = self.get_action_override(action, item) #Either no item required, item is carried by player, or item in room result_text = '' if action == Actions.PULL: result_text = self.pull(item) elif action == Actions.PUSH: result_text = self.push(item) elif action == Actions.SEARCH: result_text = self.search(item, item_text) elif action == Actions.CAST: result_text = self.cast(player, spell_text, item) elif action == Actions.THROW: result_text = self.throw(player, item, target_text) elif action == Actions.CLOSE: result_text = self.close(item) elif action == Actions.OPEN: result_text = self.open(item) elif action == Actions.LOCK: result_text = self.lock(player, item) elif action == Actions.UNLOCK: result_text = self.unlock(player, item) elif action == Actions.DESCRIBE: result_text = self.describe(item) elif action == Actions.NO: result_text = self.reply_no() elif action == Actions.YES: result_text = self.reply_yes() elif action == Actions.PICK_LOCK: result_text = self.pick_lock(item) elif action == Actions.SHOOT: result_text = self.shoot(player, item) elif action == Actions.STRIKE: result_text = self.strike(player, item) elif action == Actions.WHAT: result_text = self.what_can_user_do(player) elif action == Actions.PICKUP: result_text = self.pickup(item) elif action == Actions.DROP: result_text = self.drop(item) elif action == Actions.DRINK: result_text = self.drink(player, item) elif action == Actions.WHERE: result_text = self.where() elif action == Actions.EAT: result_text = self.eat(player, item) elif action == Actions.MONEY: result_text = self.count_money(player) elif action == Actions.BUY: result_text = self.buy(item_text, amount) elif action == Actions.SELL: result_text = self.sell(item_text, amount) elif action == Actions.WHAT_BUY: result_text = self.whats_for_sale() else: result_text = ReplyHelpers.render_common_template('no_action') return QuestGameNormalReply(self, result_text + ' ' + self.__check_for_monster(player))
def eat(self, player, item): if not isinstance(item, items.Food): return ReplyHelpers.render_action_template('eat_cannot', item_text=item.description) with Observer(player) as ob: player.eat(item) return self.__build_reply(ob)