You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
4.8 KiB

  1. from random import randint, choice
  2. from .models import (
  3. CharacterClass,
  4. EquipmentArmour,
  5. EquipmentRangedWeapon,
  6. EquipmentMeleeWeapon,
  7. CharacterNPC
  8. )
  9. def number_encountered():
  10. return (randint(1, 4) + 2)
  11. def npc_class():
  12. classes = [c for cls in CharacterClass.query.all() for c in ([cls] * cls.frequency_modifier)]
  13. return choice(classes)
  14. def npc_alignment():
  15. roll = randint(1, 6)
  16. if roll == 1 or roll == 2:
  17. return 'Lawful'
  18. elif roll == 3 or roll == 4 or roll == 5:
  19. return 'Neutral'
  20. elif roll == 6:
  21. return 'Chaotic'
  22. def npc_baselevel(base_level):
  23. roll = randint(1, 6)
  24. mod = 0
  25. if roll == 1:
  26. mod = -2
  27. elif roll == 2:
  28. mod = -1
  29. elif roll == 3 or roll == 4:
  30. mod = 0
  31. elif roll == 5:
  32. mod = 1
  33. elif roll == 6:
  34. mod = 2
  35. base_level += mod
  36. if base_level < 1:
  37. base_level = 1
  38. return base_level
  39. def npc_abilities():
  40. ability_list = ['str', 'int', 'wis', 'dex', 'con', 'chr']
  41. abilities = [(randint(1, 6) + randint(1, 6) + randint(1, 6)) for x in range(0,6)]
  42. return zip(ability_list, abilities)
  43. def attribute_mod(atr):
  44. mod = 0
  45. if atr <= 3: mod = -3
  46. if atr >= 4 and atr <= 5: mod = -2
  47. if atr >= 6 and atr <= 8: mod = -1
  48. if atr >= 9 and atr <= 12: mod = 0
  49. if atr >= 13 and atr <= 15: mod = 1
  50. if atr >= 16 and atr <= 17: mod = 2
  51. if atr >= 18: mod = 3
  52. return mod
  53. def select_melee_weapon(guild, data):
  54. weapons = []
  55. for x in range(0, guild.melee_heavy):
  56. weapons.extend(data['melee']['heavy'])
  57. for x in range(0, guild.melee_medium):
  58. weapons.extend(data['melee']['medium'])
  59. for x in range(0, guild.melee_light):
  60. weapons.extend(data['melee']['light'])
  61. selection = randint(0, len(weapons) - 1)
  62. return weapons[selection]
  63. def select_ranged_weapon(guild, data):
  64. weapons = []
  65. for x in range(0, guild.ranged_light):
  66. weapons.extend(data['ranged']['light'])
  67. for x in range(0, guild.ranged_heavy):
  68. weapons.extend(data['ranged']['heavy'])
  69. if not weapons:
  70. return None
  71. selection = randint(0, len(weapons) - 1)
  72. return weapons[selection]
  73. def generate_npc(base_level, data):
  74. guild = npc_class()
  75. npc = {}
  76. npc['guild'] = guild.name
  77. npc['level'] = npc_baselevel(base_level)
  78. npc.update(npc_abilities())
  79. npc['hp'] = 0
  80. conmod = attribute_mod(npc['con'])
  81. hitdice = [randint(1, guild.hit_die_size) for x in range(0, npc['level'])]
  82. for die in hitdice:
  83. die += conmod
  84. if die < 1:
  85. die = 1
  86. npc['hp'] += die
  87. armourval = randint(0, len(data['armours']) - 1) + guild.armour_modifier
  88. if armourval < 0:
  89. armourval = 0
  90. if armourval > len(data['armours']) - 1:
  91. armourval = len(data['armours']) - 1
  92. armour = data['armours'][armourval]
  93. npc['armour'] = [armour.name, armour.gp_value, armour.ac_mod]
  94. melee = select_melee_weapon(guild, data)
  95. npc['melee'] = [melee.name, melee.gp_value, melee.damage_die] if melee else None
  96. ranged = select_ranged_weapon(guild, data)
  97. npc['ranged'] = [ranged.name, ranged.gp_value, ranged.damage_die] if ranged else None
  98. return npc
  99. def create_party(base_level):
  100. data = {
  101. 'armours': EquipmentArmour.query.all(),
  102. 'ranged': {
  103. 'light': EquipmentRangedWeapon.query.filter_by(bucket='Light').all(),
  104. 'heavy': EquipmentRangedWeapon.query.filter_by(bucket='Heavy').all()
  105. },
  106. 'melee': {
  107. 'light': EquipmentMeleeWeapon.query.filter_by(bucket='Light').all(),
  108. 'medium': EquipmentMeleeWeapon.query.filter_by(bucket='Medium').all(),
  109. 'heavy': EquipmentMeleeWeapon.query.filter_by(bucket='Heavy').all()
  110. },
  111. }
  112. party_size = number_encountered()
  113. return [generate_npc(base_level, data) for x in range(0, party_size)]
  114. def print_party(party):
  115. def print_npc(npc):
  116. print('Level {0} NPC, {1}, {2} HP'.format(npc['level'], npc['guild'], npc['hp']))
  117. print('{0} Str, {1} Int, {2} Wis, {3} Dex, {4} Con, {5} Chr'.format(
  118. npc['str'], npc['int'], npc['wis'],
  119. npc['dex'], npc['con'], npc['chr']
  120. ))
  121. print('Armour Class: {0} - {1}, {2}gp'.format(npc['armour'][2], npc['armour'][0], npc['armour'][1]))
  122. print('{:^16} - {:^10} - {:^10} - {:^10}'.format('Weapon', 'Gold', 'Throw Mod', 'Damage'))
  123. print('-------------------------------------------------------')
  124. print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc['melee'][0], npc['melee'][1], 0, npc['melee'][2]))
  125. if npc['ranged']:
  126. print('{:^16} | {:^10} | {:^10} | {:^10}'.format(npc['ranged'][0], npc['ranged'][1], 0, npc['ranged'][2]))
  127. print('\n')
  128. for npc in party:
  129. print_npc(npc)
  130. if __name__ == '__main__':
  131. party = create_party(2)
  132. print_party(party)