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 wound(self, points, critical_hit=False): if points <= 0 or self.is_dead: return False self.notify_observers_log("{} wounded {}".format( self.__class__.__name__, points)) #Dead if massive damage, otherwise unconsious #10 - 20 = -10 - dead if (self.hit_points - points) <= -self.max_hit_points: self.__is_dead = True self.notify_observers_log("{} - DIED!".format(self.class_name)) starting_health = self.hit_points self.__set_hit_points(self.hit_points - points) if self.is_unconscious: death_saves = 3 if starting_health == 0: death_saves -= 1 if critical_hit: death_saves -= 1 else: self.notify_observers_log("{} - UNCONSCIOUS!".format( self.class_name)) #Roll saves saved = next((True for x in range(death_saves) if GameRules.roll_death_save(self)), False) if not saved: self.__is_dead = True self.notify_observers_log("{} - DIED!".format(self.class_name)) return True
def get_attribute_modifier(self, attrs): if not isinstance(attrs, list): attrs = [attrs] #If multiple select max if len(attrs) == 0: return 0 max_attr_val = max(map(self.get_attribute_current, attrs)) return GameRules.get_attribute_modifier(max_attr_val)
def check_for_trap(self, player, trap): ''' Player checks to determine if trap is set or not Perception check ''' roll = GameRules.roll_perception_check(player) return roll.total >= trap.difficulty_class.value
def test_GameRules(self): self.assertEqual(-5, GameRules.get_attribute_modifier(0)) self.assertEqual(-5, GameRules.get_attribute_modifier(1)) self.assertEqual(1, GameRules.get_attribute_modifier(12)) self.assertEqual(1, GameRules.get_attribute_modifier(13)) self.assertEqual(2, GameRules.get_attribute_modifier(14)) self.assertEqual(2, GameRules.get_attribute_modifier(15)) self.assertEqual(10, GameRules.get_attribute_modifier(30)) self.assertEqual(10, GameRules.get_attribute_modifier(31))
def _miss(self, player, defender): Weapon._miss(self, player, defender) #Drop arrow #TODO: Arrow should be left on ground if GameRules.is_thrown_item_lost(): player.inventory.remove(self.__ammo.__class__()) else: player.get_rid_of_one(self.__ammo.__class__())
def test_Attack(self): player = Thief() #+2 attack bonus at level 1 player.strength_base = 17 #+3 strength modifier player.equip_weapon(weapons.Dagger()) #+2 proficiency for _i in range(100): dice_roll = GameRules.roll_weapon_attack_score(player) #critical_strike = player.get_equipped_weapon().is_critical_strike(dice_roll.roll) print("Player Attack score: {}".format(dice_roll.total)) if not player.get_equipped_weapon().is_critical_miss( dice_roll.roll): self.assertGreaterEqual(dice_roll.total, 7) self.assertLessEqual(dice_roll.total, 27) player.add_bonus('POTION', PlayerAttributes.STRENGTH, 4) #21 strength, +5 strength modifer for _i in range(100): dice_roll = GameRules.roll_weapon_attack_score(player) #critical_strike = player.get_equipped_weapon().is_critical_strike(dice_roll.roll) print("Player Attack score: {}".format(dice_roll.total)) if not player.get_equipped_weapon().is_critical_miss( dice_roll.roll): self.assertGreaterEqual(dice_roll.total, 9) self.assertLessEqual(dice_roll.total, 29) player.remove_bonus('POTION') player.equip_weapon(weapons.LongBow()) #0 proficiency player.dexterity_base = 0 #-5 dexterity modifier, +2 attack bonus for _i in range(100): dice_roll = GameRules.roll_weapon_attack_score(player) #critical_strike = player.get_equipped_weapon().is_critical_strike(dice_roll.roll) print("Player Attack score: {}".format(dice_roll.total)) if not player.get_equipped_weapon().is_critical_miss( dice_roll.roll): self.assertGreaterEqual(dice_roll.total, -2) self.assertLessEqual(dice_roll.total, 17) player.dexterity_base = 12 #+1 dexterity modifier for _i in range(100): dice_roll = GameRules.roll_weapon_attack_score(player) #critical_strike = player.get_equipped_weapon().is_critical_strike(dice_roll.total) print("Player Attack score: {}".format(dice_roll.total)) if not player.get_equipped_weapon().is_critical_miss( dice_roll.roll): self.assertGreaterEqual(dice_roll.total, 4) self.assertLessEqual(dice_roll.total, 23)
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 unlock(self, player, key=False, spell=False): if not self.is_locked: self.notify_observers_log('{} is not locked'.format(self.name)) self.notify_observers_reply( ReplyHelpers.render_room_template('not_locked', item=self.name)) return False if key: if key.id == self._key_id: self._is_locked = False self.notify_observers_log('{} is unlocked'.format(self.name)) return True #Wrong key self.notify_observers_log('Wrong key used for {}'.format( self.name)) return False if spell: if spell.__class__ == spells.UnlockSpell: self._is_locked = False self.notify_observers_log('{} is unlocked'.format(self.name)) self.notify_observers_reply( ReplyHelpers.render_room_template('spell_success', action='unlocked', target_name=self.name)) return True #Wrong spell self.notify_observers_log('Wrong spell used for {}'.format( self.name)) #Picklock if not player.current_action == Actions.PICK_LOCK: #Logic error self.notify_observers_log('Can' 't pick lock, player doesn' 't seem to be performing this action') return False #pick lock skill_roll = GameRules.roll_skill_check(player, skills.LockPicking()) if skill_roll.total >= self.lock_resistance.value: self._is_locked = False self.notify_observers_reply( ReplyHelpers.render_room_template('picklock_success', item=self.name)) return True #pick failed self.notify_observers_reply( ReplyHelpers.render_room_template('picklock_fail', item=self.name)) return False
def _hit(self, caster, target): #If casting on one self it always hits if caster == target: return True defense_target = self.spell_difficulty_class(caster) saving_roll = GameRules.roll_saving_spell_throw(self, target) from questgame.players.players import Player if isinstance(target, Player): save = saving_roll.total caster.notify_observers_log('Defense: {}, Saving: {}({})'.format(defense_target, saving_roll.total, saving_roll.roll)) else: save = target.spell_resistance caster.notify_observers_log('Defense: {}, Saving: {}'.format(defense_target, save)) hit = save < 20 and save <= defense_target if not hit: caster.notify_observers_log('{} failed on {}'.format(self.__class__.__name__,target.__class__.__name__)) return hit
def __init__(self, character_class): super(Player, self).__init__(None) Observable.__init__(self) self.__hit_points = 0 self.__max_hit_points = 0 self.__mana_points = 0 self.__max_mana_points = 0 self.__attributes = {} self.__learned_spells = [] self.__default_weapon = PlayerStats.get_weapon(self) self.__default_armor = PlayerStats.get_armor(self) self.__armor = None self.__weapon = None self.__character_class = character_class self.__experience = 0 self.__level = Level() self.__is_stunned = False self.__is_in_battle = False self.__is_dead = False self.__is_looted = False self.__current_action = False self.__inventory = Inventory(self) attrs = PlayerStats.get_attributes(self) self.strength_base = attrs[PlayerAttributes.STRENGTH] self.dexterity_base = attrs[PlayerAttributes.DEXTERITY] self.constitution_base = attrs[PlayerAttributes.CONSITUTION] self.wisdom_base = attrs[PlayerAttributes.WISDOM] self.intelligence_base = attrs[PlayerAttributes.INTELLIGENCE] self.charisma_base = attrs[PlayerAttributes.CHARISMA] self.equip_weapon(self.__default_weapon) self.equip_armor(self.__default_armor) hp_dice = PlayerStats.get_hp_dice(self) hp_sides = PlayerStats.get_hp_sides(self) hp_bonus = PlayerStats.get_hp_bonus(self) self.max_hit_points = GameRules.determine_hit_points( hp_dice, hp_sides, hp_bonus) self.max_mana_points = self.character_class.get_mana_points(self)
def spell_difficulty_class(self, caster): """ Determines DC saving throw is made against """ return GameRules.determine_spell_difficulty_class(self, caster)
def test_determine_carry_capacity(self): player = Mock() player.strength = 10 self.assertEqual(GameRules.determine_carry_capacity(player), player.strength * 15)
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 get_defense(self): """ Determines current armor class - base and any modifiers """ return GameRules.get_player_defense(self)
def test_goblin(self): player = players.Thief() player.equip_weapon(weapons.Dagger()) player.equip_armor(armor.LightArmor()) player.strength_base = 15 player.dexterity_base = 10 player.max_hit_points = 100 goblin = players.Goblin() goblin.affect(player, players.Effects.Heal, 0) self.assertGreaterEqual(goblin.hit_points, 2, "Hit points set") goblin.max_hit_points = 100 player_def = player.get_defense() goblin_def = goblin.get_defense() print("Player Hit Points: {}".format(player.hit_points)) print("Player def: {}".format(player_def)) print("Player ability modifier: {}".format( player.determine_ability_modifier())) print("Player proficiency modifier: {}".format( player.determine_proficiency_bonus())) print("---") print("Goblin Hit Points: {}".format(goblin.hit_points)) print("Goblin def: {}".format(goblin_def)) print("Goblin ability modifier: {}".format( goblin.determine_ability_modifier())) print("Goblin proficiency modifier: {}".format( goblin.determine_proficiency_bonus())) print("---") dice_roll = GameRules.roll_initiative_check(goblin) gob_init_check = dice_roll.total dice_roll = GameRules.roll_initiative_check(player) player_init_check = dice_roll.total print("Goblin Initiative: {} ({})".format(gob_init_check, goblin.initiative_bonus)) print("Player Initiative: {} ({})".format(player_init_check, player.initiative_bonus)) if gob_init_check > player_init_check: print("Goblin was quicker to react, and starts first") else: print("Player was quicker to react, and starts first") print("---") #Attacks a = base_classes.Observer(goblin) b = base_classes.Observer(player) done_sneak_attack = False while not goblin.is_dead and not player.is_dead: if gob_init_check < player_init_check: player.strike(goblin) if not done_sneak_attack: player.skill_attack(skills.SneakAttack(), goblin) done_sneak_attack = True goblin.strike(player) else: goblin.strike(player) player.strike(goblin) if not done_sneak_attack: player.skill_attack(skills.SneakAttack(), goblin) done_sneak_attack = True
def carry_capacity(self): return GameRules.determine_carry_capacity(self)