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.

355 lines
13 KiB

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