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.

353 lines
14 KiB

  1. {% extends "base.html" %}
  2. {% set active_page = "npcparty" %}
  3. {% block title %}NPC Party Generation{% endblock %}
  4. {% block content %}
  5. <div class="uk-flex uk-flex-center uk-margin-bottom uk-margin-top">
  6. <h1 class="uk-text-center"><strong>Adventurer Conqueror King</strong>NPC Party Generator</h1>
  7. </div>
  8. <div class="uk-flex uk-flex-bottom uk-flex-center uk-margin-large-bottom">
  9. <div>
  10. <label for="base_level">Base level of party to generate: </label>
  11. <input type="number" name="base_level" id="base_level" class="uk-input" value="{{ base_level if base_level else 1 }}" min="0" max="14">
  12. </div>
  13. <div class="uk-margin-left">
  14. <button class="uk-button uk-button-primary" onclick="generateParty();">Generate</button>
  15. </div>
  16. </div>
  17. <hr class="uk-divider-icon">
  18. {% if party %}
  19. <div class="uk-flex uk-flex-between uk-margin-large-top">
  20. <h3 class="uk-display-inline-block">NPC Party of Size {{ party | length }} </h3>
  21. <div class="uk-text-right uk-display-inline-block">
  22. <a class="uk-button uk-button-small uk-button-secondary uk-border-rounded" onclick="showExportModal();">Roll20 Export</a>
  23. </div>
  24. </div>
  25. <div class="uk-grid-medium uk-grid-match uk-flex-center" uk-grid>
  26. {% for npc in party %}
  27. <div class="acks-npc-card">
  28. <div class="uk-card uk-card-body uk-card-default uk-box-shadow-hover-large">
  29. <h4 class="uk-card-title uk-margin-small-top">{{ npc.name }}</h4>
  30. <div class="uk-card-badge uk-label uk-label-primary">{{ npc.guild.name }}</div>
  31. <div class="uk-flex uk-flex-around uk-text-center uk-margin-bottom">
  32. <div>
  33. <div class="uk-text-bold">Level</div>
  34. <div>{{ npc.level }}</div>
  35. </div>
  36. <div>
  37. <div class="uk-text-bold">HP</div>
  38. <div>{{ npc.hp }}</div>
  39. </div>
  40. <div>
  41. <div class="uk-text-bold">AC</div>
  42. <div>{{ npc.armour.ac_mod }}</div>
  43. </div>
  44. </div>
  45. <div class="uk-flex uk-flex-around stat-block">
  46. <div>
  47. <div title="{{ npc.str_mod }}">{{ npc.str }}</div>
  48. <div title="{{ npc.str_mod }}">Str</div>
  49. </div>
  50. <div>
  51. <div title="{{ npc.int_mod }}">{{ npc.int }}</div>
  52. <div title="{{ npc.int_mod }}">Int</div>
  53. </div>
  54. <div>
  55. <div title="{{ npc.wis_mod }}">{{ npc.wis }}</div>
  56. <div title="{{ npc.wis_mod }}">Wis</div>
  57. </div>
  58. <div>
  59. <div title="{{ npc.dex_mod }}">{{ npc.dex }}</div>
  60. <div title="{{ npc.dex_mod }}">Dex</div>
  61. </div>
  62. <div>
  63. <div title="{{ npc.con_mod }}">{{ npc.con }}</div>
  64. <div title="{{ npc.con_mod }}">Con</div>
  65. </div>
  66. <div>
  67. <div title="{{ npc.chr_mod }}">{{ npc.chr }}</div>
  68. <div title="{{ npc.chr_mod }}">Chr</div>
  69. </div>
  70. </div>
  71. <hr class="uk-divider-small uk-text-center">
  72. <div class="uk-flex uk-flex-around uk-text-center uk-margin-bottom save-block uk-margin-top">
  73. <div>
  74. <div title="Petrification & Paralysis">{{ npc.save_pp }}</div>
  75. <div title="Petrification & Paralysis">P &amp; P</div>
  76. </div>
  77. <div>
  78. <div title="Poison & Death">{{ npc.save_pd }}</div>
  79. <div title="Poison & Death">P &amp; D</div>
  80. </div>
  81. <div>
  82. <div title="Blast & Breath">{{ npc.save_bb }}</div>
  83. <div title="Blast & Breath">B &amp; B</div>
  84. </div>
  85. <div>
  86. <div title="Staffs & Wands">{{ npc.save_sw }}</div>
  87. <div title="Staffs & Wands">S &amp; W</div>
  88. </div>
  89. <div>
  90. <div title="Spells">{{ npc.save_sp }}</div>
  91. <div title="Spells">Spells</div>
  92. </div>
  93. </div>
  94. <ul uk-tab>
  95. <li class="uk-active"><a href="">Equipment</a></li>
  96. <li {% if not npc.is_divine_spellcaster and not npc.is_arcane_spellcaster %}class="uk-disabled"{% endif %}><a href="">Spells</a></li>
  97. </ul>
  98. <ul class="uk-switcher uk-margin">
  99. <li>
  100. <table class="uk-table uk-table-hover uk-table-small item-table">
  101. <thead>
  102. <tr> <th>Name</th><th>Worth</th><th>Thr</th><th>Dmg</th> </tr>
  103. </thead>
  104. <tbody>
  105. <tr>
  106. <td>{{ npc.melee.name }}</td>
  107. <td>{{ npc.melee.gp_value }}gp</td>
  108. <td>{{ npc.attack_throw }}</td>
  109. <td>{{ npc.melee.damage_die }}</td>
  110. </tr>
  111. {% if npc.ranged %}
  112. <tr>
  113. <td>{{ npc.ranged.name }}</td>
  114. <td>{{ npc.ranged.gp_value }}gp</td>
  115. <td>{{ npc.attack_throw }}</td>
  116. <td>{{ npc.ranged.damage_die }}</td>
  117. </tr>
  118. {% endif %}
  119. <tr>
  120. <td>{{ npc.armour.name }}</td>
  121. <td>{{ npc.armour.gp_value }}gp</td>
  122. <td></td>
  123. <td></td>
  124. </tr>
  125. </tbody>
  126. </table>
  127. </li>
  128. <li>
  129. <table class="uk-table uk-table-hover uk-table-small spell-table">
  130. <thead>
  131. <tr> <th>Name</th><th>Range</th><th>Duration</th><th>Level</th> </tr>
  132. </thead>
  133. <tbody>
  134. {% for level in npc.spell_list() %}
  135. {% for spell in npc.spell_list()[level] %}
  136. <tr>
  137. <td data-spell-id="{{ spell.id }}" onclick="showSpellModal();">{{ spell.name }}</td>
  138. <td>{{ spell.range }}</td>
  139. <td>{{ spell.duration }}</td>
  140. <td>{{ spell.level_for(npc) }}</td>
  141. </tr>
  142. {% endfor %}
  143. {% endfor %}
  144. </tbody>
  145. </table>
  146. </li>
  147. </ul>
  148. </div>
  149. </div>
  150. {% endfor %}
  151. </div>
  152. {% endif %}
  153. <div id="spell-modal" uk-modal>
  154. <div class="uk-modal-dialog uk-margin-auto-vertical">
  155. <button class="uk-modal-close-default" type="button" uk-close></button>
  156. <div class="uk-modal-header">
  157. <h2 class="uk-modal-title"><span id="spell-modal-title">{Spell Title}</h2>
  158. </div>
  159. <div class="uk-modal-body">
  160. <ul>
  161. <li>Range: <span id="spell-modal-range">{Spell Range}</span></li>
  162. <li>Duration: <span id="spell-modal-duration">{Spell Duration}</span></li>
  163. <li><span id="spell-modal-school">{Spell School}</span></li>
  164. </ul>
  165. <p id="spell-modal-desc">{Spell Description}</p>
  166. </div>
  167. </div>
  168. </div>
  169. <div id="party-export-modal" uk-modal>
  170. <div class="uk-modal-dialog uk-margin-auto-vertical">
  171. <button class="uk-modal-close-default" type="button" uk-close></button>
  172. <div class="uk-modal-header">
  173. <h2 class="uk-modal-title">Roll20 Export</h2>
  174. </div>
  175. <div class="uk-modal-body">
  176. <div class="uk-margin">
  177. <h5>Characters to Export:</h5>
  178. <div id="pe_selects" class="uk-grid-small uk-grid uk-child-width-auto">
  179. {% if party %}
  180. {% for npc in party %}
  181. <label><input class="uk-checkbox" type="checkbox" name="ch{{ loop.index }}" onchange="prepareSelectiveExport();" checked> {{ npc.name }}</label>
  182. {% endfor %}
  183. {% endif %}
  184. </div>
  185. <div class="uk-margin uk-align-right">
  186. <div class="uk-button-group">
  187. <button id="pe_none" class="uk-button uk-button-small uk-button-default" onclick="partyExportToggle();">None</button>
  188. <button id="pe_all" class="uk-button uk-button-small uk-button-secondary" onclick="partyExportToggle();">All</button>
  189. </div>
  190. </div>
  191. </div>
  192. <textarea id="party-export-show" class="uk-textarea uk-text-small" rows="10"></textarea>
  193. <textarea id="party-export-data" class="uk-textarea uk-hidden"></textarea>
  194. </div>
  195. <div class="uk-modal-footer uk-text-right">
  196. <button class="uk-button uk-button-default uk-modal-close" type="button">Cancel</button>
  197. <button class="uk-button uk-button-primary" type="button" onclick="exportParty();">Copy</button>
  198. </div>
  199. </div>
  200. </div>
  201. <br>
  202. <style>
  203. table.item-table, table.item-table th, table.spell-table, table.spell-table th {
  204. font-size: 12px;
  205. }
  206. div.stat-block > div {
  207. text-align: center;
  208. padding: 3px;
  209. width: 26px;
  210. }
  211. div.stat-block > div > div:first-child {
  212. font-weight: bold;
  213. color: green;
  214. }
  215. div.stat-block > div > div:last-child {
  216. font-weight: bold;
  217. font-size: 12px;
  218. color: #888;
  219. }
  220. div.save-block > div > div:first-child {
  221. font-weight: bold;
  222. color: purple;
  223. }
  224. div.save-block > div > div:last-child {
  225. font-weight: bold;
  226. font-size: 12px;
  227. color: #888;
  228. }
  229. div.acks-npc-card {
  230. width: 400px;
  231. }
  232. h1 strong {
  233. display: block;
  234. font-size: 50%;
  235. opacity: 0.65;
  236. }
  237. </style>
  238. <script type="text/javascript">
  239. var party = [];
  240. function generateParty() {
  241. let bl = document.querySelector('#base_level').value;
  242. if(bl < 0) { bl = 0; }
  243. if(bl > 14) { bl = 14; }
  244. window.location = "/npc/party/" + bl.toString();
  245. }
  246. function showSpellModal() {
  247. spells = new Set();
  248. {% if party %}
  249. {% for npc in party %}
  250. {% for level in npc.spell_list() %}
  251. {% for spell in npc.spell_list()[level] %}
  252. spells.add({{ spell.roll20_format | tojson }});
  253. {% endfor %}
  254. {% endfor %}
  255. {% endfor %}
  256. {% endif %}
  257. var modal_spell, spell_id = event.target.dataset.spellId;
  258. for(var spell of spells) {
  259. if(spell.id == spell_id) {
  260. modal_spell = spell
  261. }
  262. }
  263. // Fill in spell details
  264. document.querySelector('#spell-modal-title').innerText = modal_spell.name;
  265. document.querySelector('#spell-modal-desc').innerText = atob(modal_spell.description);
  266. if(spell.range) {
  267. document.querySelector('#spell-modal-range').innerText = modal_spell.range;
  268. }
  269. if(spell.duration) {
  270. document.querySelector('#spell-modal-duration').innerText = modal_spell.duration;
  271. }
  272. var school = '';
  273. if(modal_spell.is_arcane) {
  274. school += 'Arcane ' + modal_spell.arcane + ' ';
  275. }
  276. if(modal_spell.is_divine) {
  277. school += 'Divine ' + modal_spell.divine + ' ';
  278. }
  279. document.querySelector('#spell-modal-school').innerText = school;
  280. UIkit.modal(document.querySelector('#spell-modal')).show();
  281. }
  282. function showExportModal() {
  283. {% if party %}
  284. {% for npc in party %}
  285. party.push(JSON.parse('{{ npc.roll20_format | tojson }}'));
  286. {% endfor %}
  287. {% endif %}
  288. for(let cb of document.querySelectorAll('#pe_selects > label > input')) {
  289. cb.checked = true;
  290. }
  291. prepareSelectiveExport();
  292. UIkit.modal(document.querySelector('#party-export-modal')).show();
  293. }
  294. function prepareSelectiveExport() {
  295. var selected_party = [];
  296. var checkboxes = document.querySelectorAll('#pe_selects > label > input');
  297. for(let [i, cb] of checkboxes.entries()) {
  298. if(cb.checked) {
  299. selected_party.push(party[i]);
  300. }
  301. }
  302. document.querySelector('#party-export-data').value = "!acksimport " + JSON.stringify(selected_party);
  303. document.querySelector('#party-export-show').value = JSON.stringify(selected_party, undefined, 2);
  304. }
  305. function exportParty() {
  306. let party_data = document.querySelector('#party-export-data')
  307. party_data.classList.remove('uk-hidden');
  308. party_data.select();
  309. document.execCommand("copy");
  310. party_data.classList.add('uk-hidden');
  311. UIkit.modal(document.querySelector('#party-export-modal')).hide();
  312. }
  313. function partyExportToggle() {
  314. var checkboxes = document.querySelectorAll('#pe_selects > label > input');
  315. for(let cb of checkboxes) {
  316. if(event.target.id === "pe_none") {
  317. cb.checked = false;
  318. }
  319. if(event.target.id === "pe_all") {
  320. cb.checked = true;
  321. }
  322. }
  323. prepareSelectiveExport();
  324. }
  325. </script>
  326. {% endblock %}