Exemplo n.º 1
0
 def __init__(self, cr, race=None, pc_class=None):
     super().__init__(cr, race)
     self.profession = "Mercenary"
     if pc_class is None:
         self.npc_class = self.pc_classes()
     else:
         self.npc_class = pc_class
     self.level = smart_clamp(cr - 1, 1, 20)
     self.player = Player(self.npc_class, self.level)
     self.stats = self.player.stats
     self.stat_mod = self.player.stat_mod
     self.fav_stat_mod = self.stat_mod[self.npc_class.fav_stat]
     self.magic_bonus = self.player.magic_bonus
     x = self.player.level
     y = self.class_price_multi(self.player.cls.name)
     z = self.fav_stat_mod + self.magic_bonus
     self.price = smart_clamp(x * y * z, x, 99999)
     self.name = f"NPC {self.player.cls.name}"
     self.damage_bonus = self.stat_mod[
         self.npc_class.fav_stat] + self.player.magic_bonus
     self.damage_type = self.player.damage_type
     self.special_dam_type = self.player.cls.damage_type
     self.damage_formula = self.player.damage_formula
     self.special_dam = f"{Monster(self.level).damage}"
     self.skills = SkillSet(self.level, self.player.prof_bonus, self.stats,
                            self.background)
     self.inventory = Loot()
     while len(self.inventory.magic_items) < CR(self.cr).tier:
         self.inventory.magic_items = career_loot(1, self.level - 1,
                                                  self.level).magic_items
     if len(self.inventory.magic_items) > self.level:
         self.inventory.magic_items = self.inventory.magic_items[:self.
                                                                 level]
     self.treasure = Loot()
Exemplo n.º 2
0
 def __init__(self, cr, room_name="Any", name=""):
     cr_rand = cr + plus_or_minus_linear(2)
     self.cr = smart_clamp(cr_rand + 3 if "legendary" in name.lower() else cr_rand, -3, 30)
     item_rank = smart_clamp(self.cr + 3 // 5, 0, 3)
     self.item_name = self.items_by_rank[item_rank]() if not name else name
     super().__init__(self.cr, room_name, self.item_name)
     self.treasure = Loot()
     self.treasure.magic_items.append(f"Sentient {self.item_name}")
Exemplo n.º 3
0
 def __init__(self, cls=None, level=0, race=None, point_buy=False):
     if not cls:
         self.cls = random_character_class()() if level > 0 else Apprentice(
         )
     else:
         self.cls = cls() if level > 0 else Apprentice()
     self.level = smart_clamp(level, 0, 20)
     self.race = self.random_race() if not race else race
     self.health = 0
     self.xp = 0
     self.ac = 10
     self.prof_bonus = 0
     self.magic_bonus = 0
     self.save_dc = 0
     self.att_bonus = 0
     self.stats = dict(
         self.cls.stats) if point_buy else self.set_random_stats()
     self.set_racial_bonuses()
     self.stat_mod = {}
     self.stat_mod_str = {}
     self.set_stat_mods()
     self.set_ac()
     self.set_xp()
     self.set_health()
     self.prof_bonus_str = ""
     self.set_prof()
     self.set_save_dc()
     self.set_att_bonus()
     self.saves = list(self.cls.save_prof)
     self.saving_throws = self.set_saves()
     self.background = random_background()
     self.loot = Loot()
     dam_mod = smart_clamp(self.level // 4, 1, 4)
     dam_bonus = self.stat_mod[self.cls.fav_stat] + self.magic_bonus
     self.average_damage = (dam_mod *
                            (self.cls.damage_dice // 2)) + dam_bonus
     self.damage_formula = f"{dam_mod}d10 + {dam_bonus}"
     self.special_damage_formula = f"{smart_clamp(self.level // 2, 1, 10)}d8 + {10 + dam_bonus}"
     self.damage_type = self.find_dam_type()
     self.special_damage_type = self.cls.damage_type
     self.loot = get_loot(self.level)
     self.treasures = set()
     if self.level > 0:
         for _ in range(smart_clamp(self.level - 5, 1, 10)):
             if percent_true(1):
                 self.treasures.add(magic_table_by_cr(self.level + 10))
             elif percent_true(50):
                 self.treasures.add(magic_table_by_cr(self.level))
             elif len(self.treasures) < 1:
                 self.treasures.add(magic_table_by_cr(self.level - 10))
Exemplo n.º 4
0
 def __init__(self,
              cr,
              dungeon_name=None,
              dungeon_size=4,
              wilderness_size=1,
              settlement_size=1):
     self.cr = smart_clamp(cr, -3, 30)
     self.npc = Npc(self.cr)
     self.race = self.npc.race
     self.appearance = self.npc.appearance
     self.mannerism = self.npc.mannerism
     self.background = self.npc.background
     self.objective = self.trinkets() if percent_true(
         50) else self.actions()
     self.skill = self.skills()
     self.wilderness = Wilderness(self.cr,
                                  num_levels=wilderness_size,
                                  areas_per_level=10)
     self.dungeon_name = dungeon_name
     self.dungeon = Dungeon(self.cr + wilderness_size,
                            dungeon_name,
                            dungeon_levels=dungeon_size)
     self.reward = get_villain_loot(self.dungeon.threats[-1].threat.cr)
     self.reward.magic_items.append(magic_table_by_cr(self.cr))
     self.dc = self.dungeon.threats[-1].threat.save_dc
     self.city = Settlement(cr=self.cr, num_people=settlement_size * 10)
     self.total_xp = self.dungeon.total_xp + self.wilderness.total_xp
     self.summary = '\n'.join((
         f"NPC: Primary Quest Giver",
         f"Race: {self.race}",
         f"Appearance: {self.appearance}",
         f"Mannerism: {self.mannerism}",
         f"Background: {self.background}",
         f"",
         f"Quest Details:",
         f"Starting Location: {self.city.name}",
         f"Objective: {self.objective}.",
         f"Location: Beyond the {self.wilderness.name}, deep inside the {self.dungeon.name}.",
         f"Primary Villain: {self.dungeon.threats[-1].threat.name}",
         f"Quest Skill Check: {self.skill} {self.dc}",
         f"Reward: {self.reward}",
         f"",
     ))
     self.total_treasure = sum((
         self.dungeon.total_treasure,
         self.wilderness.total_treasure,
         self.city.total_treasure,
         self.reward,
     ), Loot())
Exemplo n.º 5
0
 def __init__(self, cr, room_name="Any", name=None):
     minion_cr = cr - 3 if cr < 6 else cr // 2
     minion_cr = smart_clamp(minion_cr, -3, 17)
     minion_name = name or random_monster("minion")
     super().__init__(minion_cr, room_name, minion_name)
     self.dam_type = self.find_dam_type()
     self.num_appearing = dice(2, 2)
     self.stats = {
         ability: ability_dice(7) for ability in self.abilities
     }
     self.set_stat_mods()
     self.individual_hp = max(1, int(self.total_hp // self.num_appearing))
     self.treasure = Loot()
     self.damage_dice = (
         "1d2", "1d2", "1d4", "1d4",
         "1d6", "1d6", "1d6", "1d6", "1d6",
         "1d6", "1d6", "1d6", "1d6", "1d6",
         "1d8", "1d8", "1d8", "1d8", "1d8",
         "1d10", "1d10", "1d10", "1d10", "1d10",
         "1d12", "1d12", "1d12", "1d12", "1d12",
         "1d20", "1d20", "1d20", "1d20", "1d20"
     )[minion_cr + 3]
     self.damage_bonus = (
         0, 2, 4, 6,
         8, 14, 20, 26, 32,
         40, 45, 50, 65, 70,
         75, 80, 85, 90, 95,
         100, 105, 110, 115, 120,
         140, 150, 160, 180, 200,
         260, 270, 280, 290, 300
     )[minion_cr + 3]
     actual_damage_bonus = self.damage_bonus // self.num_appearing
     if actual_damage_bonus > 0:
         self.individual_dam = f"{self.damage_dice} + {actual_damage_bonus}"
     else:
         self.individual_dam = f"{self.damage_dice}"
     self.at_will_dam = f"{smart_clamp(self.cr_val // 5, 1, 5)}d4 + {self.prof_bonus} {self.dam_type}"
Exemplo n.º 6
0
class Npc:
    professions = TruffleShuffle(npc_dict['professions'])
    races = QuantumMonty(npc_dict['races']).front_linear
    appearances = TruffleShuffle(npc_dict['appearances'])
    talents = TruffleShuffle(npc_dict['talents'])
    mannerisms = TruffleShuffle(npc_dict['mannerisms'])
    traits = TruffleShuffle(npc_dict['traits'])
    bonds = TruffleShuffle(npc_dict['bonds'])
    flaws = TruffleShuffle(npc_dict['flaws'])
    trinkets = TruffleShuffle(npc_dict['trinkets'])
    treasure = Loot()
    save_dc_list = (13, 13, 13, 13, 13, 13, 13, 14, 15, 15, 15, 16, 16, 16, 17,
                    17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 21, 21, 21,
                    22, 22, 22, 23, 24)
    racial_bonuses = {
        "Human": {
            "STR": 1,
            'DEX': 1,
            'CON': 1,
            'INT': 1,
            'WIS': 1,
            'CHA': 1
        },
        "Mountain Dwarf": {
            "STR": 2,
            'CON': 2
        },
        "Hill Dwarf": {
            "WIS": 1,
            'CON': 2
        },
        "Shout Halfling": {
            "DEX": 2,
            'CON': 1
        },
        "Lightfoot Halfling": {
            "DEX": 2,
            'CHA': 1
        },
        "Forest Gnome": {
            "INT": 2,
            'DEX': 1
        },
        "Rock Gnome": {
            "INT": 2,
            'CON': 2
        },
        "High Elf": {
            "DEX": 2,
            'INT': 1
        },
        "Wood Elf": {
            "DEX": 2,
            'WIS': 1
        },
        "Half-elf": {
            "CHA": 2,
            "DEX": 1,
            "CON": 1
        },
        "Drow": {
            "DEX": 2,
            'CHA': 1
        },
        "Half-orc": {
            "STR": 2,
            'CON': 1
        },
        "Dragonborn": {
            "STR": 2,
            'CHA': 1
        },
        "Tiefling": {
            "CHA": 2,
            'INT': 1
        },
    }

    def __init__(self, cr=1, race=None):
        self.name = "NPC"
        self.damage = 0
        self.cr = smart_clamp(cr, 1, 20)
        self.profession = self.professions()
        self.race = race if race else random_human() if percent_true(
            50) else self.races()
        self.appearance = self.appearances()
        self.talent = self.talents()
        self.mannerism = self.mannerisms()
        self.trait = self.traits()
        self.bond = self.bonds()
        self.flaw = self.flaws()
        self.trinket = self.trinkets()
        self.save_dc = self.save_dc_list[self.cr + 3]
        self.xp = 0
        self.stats = {}
        self.stat_mod = {}
        self.stat_mod_str = {}
        self._set_stats()
        self._set_racial_bonuses()
        self._set_stat_mods()
        self.background = random_background()
        self.saving_throws = self.set_saves()
        self.health = self._set_health()
        self.ac = 10 + self.stat_mod["DEX"]

    def _set_racial_bonuses(self):
        if self.race in self.racial_bonuses.keys():
            for stat, val in self.racial_bonuses[self.race].items():
                self.stats[stat] += val

    def _set_health(self):
        return smart_clamp(4 + self.stat_mod['CON'], 1, 20)

    def _set_stats(self):
        stats = ('STR', 'INT', 'DEX', 'WIS', 'CON', 'CHA')
        for ability in stats:
            self.stats[ability] = ability_dice(3)

    def _set_stat_mods(self):
        mod_by_stat = (-5, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, 0, 1, 1, 2,
                       2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10)
        for ability in self.stats:
            self.stat_mod[ability] = mod_by_stat[smart_clamp(
                self.stats[ability], 1, 30)]
            if self.stat_mod[ability] > 0:
                self.stat_mod_str[ability] = f"+{self.stat_mod[ability]}"
            else:
                self.stat_mod_str[ability] = f"{self.stat_mod[ability]}"

    def set_saves(self):
        result = []
        for key, val in self.stat_mod.items():
            if val > 0:
                result.append(f"{key} +{val}")
            else:
                result.append(f"{key} {val}")
        return result

    @property
    def ability_scores(self):
        return "\n".join(f"  {key} {val} ({self.stat_mod_str[key]})"
                         for key, val in self.stats.items())

    def __str__(self):
        self.output = (f"NPC: {self.profession}", f"Race: {self.race}",
                       f"Appearance: {self.appearance}",
                       f"Mannerism: {self.mannerism}",
                       f"Ideal: {self.background.ideal}",
                       f"Flaw: {self.background.flaw}",
                       f"Hit Points: {self.health}", f"Armor Class: {self.ac}",
                       "")
        return "\n".join(self.output)
Exemplo n.º 7
0
 def get_treasures(self):
     total = Loot()
     for room in self.threats:
         if room.treasure:
             total += room.treasure
     return total
Exemplo n.º 8
0
class Trap:
    name = "Basic"
    type = "Minor"
    spot_dc = 10
    save_dc = 11
    damage = "1d10"
    rank_0_damage = "1d2"
    rank_1_damage = "2d4"
    rank_2_damage = "3d6"
    rank_3_damage = "4d8"
    rank_4_damage = "5d10"
    random_ability = TruffleShuffle(("CON", "STR", "DEX", "INT", "WIS"))
    random_trap = FlexCat(
        {
            'bludgeoning':
            ("Falling Rocks", "Swinging Hammer", "Falling Chandelier"),
            'falling': ("Deep Pit", "Spring Loaded Floor Tile",
                        "Greased Slide", "False Floor"),
            'piercing': ("Spiked Pit", "Guided Arrow", "Flying Broken Glass",
                         "Falling Chandelier of Spikes"),
            'slashing': ("Swinging Blade", "Guillotine", "Hooks and Chains",
                         "Cloud of Daggers", "Exploding Mirror"),
            'poison': ("Poison Dart", "Noxious Gas", "Pit Vipers",
                       "Exploding Rotten Corpse"),
            'acid':
            ("Acid Spay", "Acid Pool", "Acidic Bile", "Dripping Acidic Goo"),
            'fire': ("Flame Jet", "Explosive Gas", "Lava Pit",
                     "Delayed Fireball Blast", "Inferno"),
            'lightning': ("Lightning Rod", "Tesla Coil", "Electric Trip Wire",
                          "Lightning Bolt"),
            'cold': ("Frozen Ground", "Cone of Cold", "Hail Storm",
                     "Freeze the Flesh", "Ice Lance"),
            'necrotic': ("Putrid Spores", "Cloud of Decay", "Energy Drain",
                         "Beam of Entropy"),
        },
        key_bias="truffle_shuffle",
        val_bias="truffle_shuffle")
    treasure = Loot()

    def __init__(self, cr, dam_type=None):
        self.cr = smart_clamp(cr, -3, 30)
        self.save_vs = self.random_ability()
        self.xp = self.set_xp_by_cr(self.cr)
        if dam_type:
            self.damage_type = dam_type
        else:
            self.damage_type = self.random_trap.random_cat()
        self.name = self.random_trap(self.damage_type)
        self.damage = self.set_damage(self.cr)

    def set_damage(self, cr):
        if cr < 1:
            damage = self.rank_0_damage
        elif cr < 5:
            damage = self.rank_1_damage
        elif cr < 11:
            damage = self.rank_2_damage
        elif cr < 17:
            damage = self.rank_3_damage
        else:
            damage = self.rank_4_damage
        return damage

    @staticmethod
    def set_xp_by_cr(cr):
        xp_lookup = (10, 25, 50, 100, 200, 450, 700, 1100, 1800, 2300, 2900,
                     3900, 5000, 5900, 7200, 8400, 10000, 11500, 13000, 15000,
                     18000, 20000, 22000, 25000, 33000, 41000, 50000, 62000,
                     155000, 155000, 155000, 155000, 155000, 155000, 155000,
                     155000, 155000, 155000, 155000, 155000)
        return xp_lookup[cr + 3]

    def __lt__(self, other):
        return self.cr <= other.cr

    def __str__(self):
        self.output = (
            f"{self.type} Trap, CR {cr_str(self.cr)}: {self.name}",
            f"Spot & Disarm DC {self.spot_dc}",
            f"Save vs. {self.save_vs} DC {self.save_dc} for half damage.",
            f"Damage: {self.damage} {self.damage_type}",
            f"Disarm XP: {self.xp}", "")
        return "\n".join(self.output)