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.

190 lines
5.6 KiB

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