123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724 |
- <html>
- <head>
- <style type="text/css">
- html, body {
- background-color: #886028;
- font-family: 'Sans Serif';
- }
-
- button#go, button#finalize {
- font-size: 1.3em;
- padding:5px;
- margin-right: 60px;
- }
-
- .left {
- float: left;
- }
-
- .option-list > div {
- margin-right: 40px;
- }
-
- .option-list::after {
- clear: both;
- content: "";
- display: table;
- }
-
- label, select {
- display: block;
- padding: 5 0px;
- }
-
- label > span {
- min-width: 140px;
- display: inline-block;
- font-weight: bold;
- }
-
-
- label > input {
- width: 70px;
- display: inline-block;
- text-align: right;
- padding: 3px;
- }
-
- .cost {
- text-align: right;
- }
- .avail{
- text-align: center;
- }
- .reroll {
- text-align: center;
- }
- .menu {
-
-
- }
-
- .item {
- padding: 5px;
- }
-
-
- .item-table {
- margin-top: 20px;
- border-collapse: collapse;
- }
- .item-table th {
- padding: 3px 10px;
- border: 1px solid black;
- }
-
- .item-table td {
- padding: 3px 10px;
- }
- .item-table tbody tr:nth-child(odd) {
- background-color: #997039;
- }
-
-
- </style>
- <script type="text/javascript">var module = {};</script>
- <script src="itemList.js"></script>
-
- <script type="text/javascript">
- /*
- art, gems, statues, etc.
- "mundane"
- attachment, ie. finger, shoulder, hat, etc.
- scrolls
-
- */
-
- /*
- for(var m of module.exports.minor_item_list) {
- if(!module.exports.items[m]) console.log('missing:', m);
- }
- */
-
- var prng_seed = Math.floor(Math.random() * 10000 + 10000);
-
- var wh = window.location.hash;
- var hash_opts = [];
- if(wh) {
- var params = wh.split('_');
- prng_seed = params[0];
- hash_opts = params[1].split('!');
- }
-
-
- function frand() {
- var x = Math.sin(prng_seed++) * 31337;
- return x - Math.floor(x);
- }
-
-
- function clone(obj) {
- var o = {};
- for(var k in obj) {
- var v = obj[k];
-
- if(typeof v == 'object') {
- if(v instanceof Array) o = [];
- o[k] = clone(v);
- }
- else {
- o[k] = v;
- }
- }
- return o;
- }
-
-
- var magic_ammo_rarity = {
- 1: 'uncommon',
- 2: 'rare',
- 3: 'very rare',
- }
- var magic_weapon_rarity = {
- 1: 'uncommon',
- 2: 'rare',
- 3: 'very rare',
- }
- var magic_armor_rarity = {
- 1: 'rare',
- 2: 'very rare',
- 3: 'legendary',
- }
-
- var armor_of_resistance_types = [
- "Acid",
- "Cold",
- "Fire",
- "Force",
- "Lightning",
- "Necrotic",
- "Poison",
- "Psychic",
- "Radiant",
- "Thunder",
- ];
-
- var scroll_level_data = {
- 0: {name: 'Cantrip', rarity: 'common', save_dc: 13, attack_bonus: 5},
- 1: {name: '1st', rarity: 'common', save_dc: 13, attack_bonus: 5},
- 2: {name: '2nd', rarity: 'uncommon', save_dc: 13, attack_bonus: 5},
- 3: {name: '3rd', rarity: 'uncommon', save_dc: 15, attack_bonus: 7},
- 4: {name: '4th', rarity: 'rare', save_dc: 15, attack_bonus: 7},
- 5: {name: '5th', rarity: 'rare', save_dc: 17, attack_bonus: 9},
- 6: {name: '6th', rarity: 'very rare', save_dc: 17, attack_bonus: 9},
- 7: {name: '7th', rarity: 'very rare', save_dc: 18, attack_bonus: 10},
- 8: {name: '8th', rarity: 'very rare', save_dc: 18, attack_bonus: 10},
- 9: {name: '9th', rarity: 'legendary', save_dc: 19, attack_bonus: 11},
- };
-
- // make a list of 'any' ammos
- var any_ammo_list = [];
- for(var owi in module.exports.items) {
- var ow = module.exports.items[owi];
- if(ow.type == 'ammunition' && ow.magic_item && ow.any) {
- any_ammo_list.push(ow);
- delete module.exports.items[owi];
- }
- }
-
- // fill in all the ammo
- for(var owi in module.exports.items) {
- var ow = module.exports.items[owi];
- if(!(ow.type == 'ammunition' && ow.rarity == 'common' && !ow.magic_item)) continue;
-
- for(var b = 1; b <= 3; b++) {
- var nw = clone(ow);
- nw.name += ', +' + b;
- nw.id = owi + ', +' + b;
- nw.magic_item = true;
- nw.rarity = magic_ammo_rarity[b];
- nw.attack_bonus = b;
- nw.damage_bonus = b;
-
- module.exports.items[nw.id] = nw;
- module.exports.minor_item_list.push(nw.id);
- }
-
- // "any" ammos
- for(var aw of any_ammo_list) {
- var good = 0;
- if(aw.any == 'ammunition') good = 1;
-
- if(good == 1) {
- var nw = clone(aw);
- nw.id = aw.id+ ', ' + ow.id;
- nw.name = aw.name + ', ' + ow.name;
- module.exports.items[nw.id] = nw;
- module.exports.minor_item_list.push(nw.id);
- }
- }
- }
-
- // make a list of 'any' weapons
- var any_weapon_list = [];
- for(var owi in module.exports.items) {
- var ow = module.exports.items[owi];
- if(ow.type == 'weapon' && ow.magic_item && ow.any) {
- any_weapon_list.push(ow);
- delete module.exports.items[owi];
- }
- }
-
-
- // fill in all the weapons
- for(var owi in module.exports.items) {
- var ow = module.exports.items[owi];
- if(!(ow.type == 'weapon' && ow.rarity == 'mundane' && !ow.magic_item)) continue;
-
- // normal +1/2/3 magic weapons
- for(var b = 1; b <= 3; b++) {
- var nw = clone(ow);
- nw.name += ', +' + b;
- nw.id = owi + ', +' + b;
- nw.magic_item = true;
- nw.rarity = magic_weapon_rarity[b];
- nw.attack_bonus = b;
- nw.damage_bonus = b;
-
- module.exports.items[nw.id] = nw;
- }
-
- // "any" weapons
- for(var aw of any_weapon_list) {
- var good = 0;
- if(aw.any instanceof Array) {
- for(var aa of aw.any) {
- if(aw.any == 'weapon') good = 1;
- else if(aw.any == ow.weapon_style) good = 1;
- else if(aw.any == 'sword, slashing' && ow.weapon_style == 'sword') {
- if(ow.damage.indexOf('slashing') >= 0) good = 1;
- }
- else continue;
- break;
- }
- }
- else if(aw.any == 'weapon') good = 1;
- else if(aw.any == ow.weapon_style) good = 1;
- else if(aw.any == 'sword, slashing' && ow.weapon_style == 'sword') {
- if(ow.damage.indexOf('slashing') >= 0) good = 1;
- }
-
- if(good == 1) {
- var nw = clone(aw);
- nw.id = aw.id+ ', ' + ow.id;
- nw.name = aw.name + ', ' + ow.name;
- module.exports.items[nw.id] = nw;
- }
- }
- }
-
- // fill in all the armor
- for(var oai in module.exports.items) {
- var oa = module.exports.items[oai];
- if(!(oa.type == 'armor' && oa.rarity == 'mundane' && !oa.magic_item)) continue;
-
- for(var b = 1; b <= 3; b++) {
- var na = clone(oa);
- na.name += ', +' + b;
- na.id = oai + ', +' + b;
- na.magic_item = true;
- na.rarity = magic_armor_rarity[b];
- na.ac_bonus = b;
- module.exports.items[na.id] = na;
-
- // armor of resistance
- for(var dtype of armor_of_resistance_types) {
- var na = clone(oa);
- na.name += ' of Resistance, ' + dtype;
- na.id = oai + ' of resistance, ' + dtype.toLowerCase();
- na.magic_item = true;
- na.rarity = 'rare';
- na.ac_bonus = b;
- module.exports.items[na.id] = na;
- }
- }
-
- if((oa.subtype == 'medium' || oa.subtype == 'heavy') && oa.id != 'hide') {
- // mithral
- var na = clone(oa);
- na.name = 'Mithral ' + na.name;
- na.id += 'mithral ' + oai;
- na.magic_item = true;
- na.rarity = 'uncommon';
- na.mithral = true;
- na.min_str = '-';
- delete na.stealth;
- module.exports.items[na.id] = na;
- module.exports.minor_item_list.push(na.id);
-
- // adamantium
- var na = clone(oa);
- na.name = 'Adamantine ' + na.name;
- na.id += 'adamantine ' + oai;
- na.magic_item = true;
- na.rarity = 'uncommon';
- na.adamantium = true;
- module.exports.items[na.id] = na;
- }
- }
-
- // fill in the scrolls
- for(var spi in module.exports.spell_list) {
- var sp = module.exports.spell_list[spi];
-
- var ld = scroll_level_data[sp.level];
- var sc = {
- id: "scroll of " + sp.id,
- name: "Scroll of " + sp.name,
- type: "Scroll",
- level: sp.level,
- school: sp.school,
- magic_item: true,
- rarity: ld.rarity,
- attack_bonus: ld.attack_bonus,
- save_dc: ld.save_dc,
- consumable: true,
- };
-
- module.exports.items[sc.id] = sc;
- module.exports.minor_item_list.push(sc.id);
- }
-
-
- function $all(sel, n) {
- return (n || document).querySelectorAll(sel);
- }
- function $(sel, n) {
- return (n || document).querySelector(sel);
- }
-
- function $ce(tag, classes, content) {
- var n = document.createElement(tag);
-
- if(classes) {
- if(typeof classes == 'string') n.className = classes;
- else if(classes instanceof Array) n.classList = classes;
- }
-
- if(content) {
- if(typeof content == 'string') n.innerHTML = content;
- else if(typeof content == 'number') n.innerHTML = content + '';
- else n.appendChild(content);
- }
-
- return n;
- }
-
- function roll(n, d) {
- var s = 0;
- for(var i = 0; i < n; i++) s += Math.floor((frand() * d)) + 1;
- return s;
- }
-
- function makeControl(e) {
- var l = $ce('label');
-
- var c = $ce('input');
- c.type = e.getAttribute('type');
- c.value = e.innerHTML;
- c.id = e.getAttribute('cid');
-
- if(c.type == "checkbox") {
- if(e.innerHTML == "1") {
- c.checked = true;
- }
-
- l.appendChild(c);
- l.appendChild($ce('span', '', e.getAttribute('label')));
- }
- else {
- l.appendChild($ce('span', '', e.getAttribute('label')));
- l.appendChild(c);
- }
-
- e.parentNode.insertBefore(l, e);
-
- e.parentNode.removeChild(e);
- }
-
-
- function is_minor(x) {
- return module.exports.minor_item_list.indexOf(x) >= 0;
- }
-
-
-
- function getPrice(item, q) {
- var x;
- switch(item.rarity) {
- case 'mundane': x = q + 1; break;
- case 'common': x = q * 10; break;
- case 'uncommon': x = q * 100; break;
- case 'rare': x = q * 1000; break;
- case 'very rare': x = q * 10000; break;
- case 'legendary': x = (roll(1, 6) + q) * 25000; break;
- case 'artifact': x = (roll(2, 6) + q) * 250000; break;
- };
-
- if(item.consumable == true) x = Math.max(1, Math.floor((x / 2) + 0.5));
-
- return x;
- }
-
- function nfmt(s) {
- return String(s).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
- }
-
- function filterType(type, list, special) {
- for(var ind in list) {
- var i = list[ind];
- if(i.type == type) {
- special[type].push(i);
- list.splice(ind, 1);
- }
- }
- }
-
-
- function generateList(opts) {
-
- var cont = $('.item-list');
- cont.innerHTML = '';
-
-
- function qroll() {
- return roll(opts.qdice[0], opts.qdice[1]);
- }
-
- function filterItems(rarity, minor) {
- var o = [];
- for(var k in module.exports.items) {
- var i = module.exports.items[k];
- if(opts.exclude_cursed && i.cursed) continue;
- if(opts.include.PH == false && i.src == 'PH') continue;
- if(opts.include.DM == false && i.src == 'DM') continue;
- if(opts.include.XG == false && i.src == 'XG') continue;
- if(opts.include.EE == false && i.src == 'EE') continue;
- if(
- i.rarity == rarity
- && (is_minor(i.id) == minor)
- && i.magic_item
- ) {
- if(opts.exclude_pocket && i.pocket_dimension) continue;
-
- o.push(i);
- }
- }
-
- return o;
- }
-
-
- function addItem(item, list) {
- var c = $ce('tr', 'item');
-
- var qty = qroll();
- var price = 100 * opts.pmul;
-
- var n_ = $ce('td', 'name', item.name);
- var r_ = $ce('td', 'rarity', item.rarity);
- var q_ = $ce('td', 'avail', qroll());
- var p_ = $ce('td', 'cost', nfmt(getPrice(item, qty)));
-
- var rm = $ce('button', '', 'X');
- rm.addEventListener('click', function(e) {
- if(list.length == 0) {
- c.remove();
- return;
- };
- var ind = Math.floor(frand() * list.length);
-
- var it = list[ind];
- var q = qroll();
-
- n_.innerHTML = it.name;
- r_.innerHTML = it.rarity;
- q_.innerHTML = q;
- p_.innerHTML = nfmt(getPrice(it, q));
-
- list.splice(ind, 1);
- });
-
- c.appendChild(n_);
- c.appendChild(r_);
- c.appendChild(q_);
- c.appendChild(p_);
- c.appendChild(rm);
-
- c.appendChild($ce('td', 'reroll', rm))
-
- cont.appendChild(c);
- }
-
-
- var item_res = {};
-
- for(var sz in module.exports.standard_awards) {
- var z = module.exports.standard_awards[sz];
-
- for(var r in z) {
- var k = sz + '_' + r;
- var list = filterItems(r, sz == 'minor');
- item_res[k] = list;
-
- var amt = Math.floor((z[r][opts.CR] * opts.icmul) + .5);
- var chosen = [];
- var weapon_cnt = 0;
- var armor_cnt = 0;
- var scrolls_cnt = 0;
-
- var special = {
- weapon: [],
- armor: [],
- Scroll: [],
- };
-
- for(var n = 0; n < amt; n++) {
- if(list.length == 0) break;
- var ind = Math.floor(frand() * list.length);
-
- var it = list[ind];
-
- if(it.type == 'weapon') {
- if(++weapon_cnt > opts.max_weapons) {
- filterType('weapon', list, special);
- n--;
- continue;
- }
- }
-
- if(it.type == 'armor') {
- if(++armor_cnt > opts.max_armor) {
- filterType('armor', list, special);
- n--;
- continue;
- }
- }
-
- if(it.type == 'Scroll') {
- if(++scrolls_cnt > opts.max_scrolls) {
- filterType('Scroll', list, special);
- n--;
- continue;
- }
- }
-
- chosen.push(it);
- addItem(it, list, special);
-
- list.splice(ind, 1);
- // console.log(n, ind, list);
- }
-
- // console.log(sz, r, amt, list.length, chosen);
- }
-
-
- }
- }
-
-
- window.addEventListener("load", function() {
-
- if(wh) {
-
- generateList({
- exclude_pocket: hash_opts[0] == 1,
- max_weapons: hash_opts[1]|0,
- max_armor: hash_opts[2]|0,
- max_scrolls: hash_opts[3]|0,
- icmul: hash_opts[4]|0,
- pmul: hash_opts[5]|0,
- qdice: hash_opts[6].split('d'),
- CR: hash_opts[7]|0,
- });
-
- $('.menu').remove();
- $all('.reroll').forEach(function(x) { x.remove() });
-
- return;
- }
-
-
- $all('control').forEach(makeControl);
-
-
- $('#finalize').addEventListener('click', function(e) {
- $('.menu').remove();
- $all('.reroll').forEach(function(x) { x.remove() });
- });
-
- $('#go').addEventListener('click', function(e) {
- e.preventDefault();
-
- $('#finalize').style.display = 'inline-block';
-
- var prng_seed_orig = prng_seed;
-
- // var raw = $('#item-count').value.toLowerCase();
- // var [n,d] = raw.split('d');
-
- /* for checking the dice algorithm
- var o = {};
- for(var i = 0; i < n+2; i ++) o[i] = 0;
- for(var i = 0; i < 1000000; i ++){
- o[roll(n,d)]++;
- }
- console.log(o);
- */
-
- var CRn = $('#tier');
-
- var o = {
- exclude_pocket: $('#exclude-pockets').checked,
- exclude_cursed: $('#exclude-cursed').checked,
- max_weapons: parseFloat($('#max-weapons').value),
- max_armor: parseFloat($('#max-armor').value),
- max_scrolls: parseFloat($('#max-scrolls').value),
- icmul: parseFloat($('#item-count').value),
- pmul: parseFloat($('#price-mul').value),
- qdice: $('#avail-dice').value.split('d'),
- CR: CRn.selectedOptions[0].value - 1,
- include: {
- PH: $('#include-PH').checked,
- DM: $('#include-DM').checked,
- XG: $('#include-XG').checked,
- EE: $('#include-EE').checked,
- },
- };
-
- generateList(o);
-
- // window.location.hash = prng_seed_orig + '_'
- // + (o.exclude_pocket ? '1' : '0') + '!'
- })
-
- });
-
-
- </script>
- </head>
- <body>
- <div class="menu">
- <select id="tier">
- <option value="1">Tier 1: 1-4</option>
- <option value="2" selected>Tier 2: 5-10</option>
- <option value="3">Tier 3: 11-16</option>
- <option value="4">Tier 4: 17-20</option>
- </select>
-
- <div class="option-list">
- <div class="left">
- <control cid="item-count" label="Item Count Mul.:" type="edit">1</control>
- <control cid="price-mul" label="Price Multiplier:" type="edit">1</control>
- <control cid="avail-dice" label="Avail/Price:" type="edit">1d6</control>
- <!--</div>
- <div class="left">-->
- <hr/>
- <label><span>Per Rarity:</span></label>
- <control cid="max-weapons" label="Max Weapons:" type="edit">2</control>
- <control cid="max-armor" label="Max Armor:" type="edit">2</control>
- <control cid="max-scrolls" label="Max Scrolls:" type="edit">2</control>
-
- <hr/>
- <control cid="exclude-pockets" label="Exclude Pocket Dimension" type="checkbox">1</control>
- <control cid="exclude-cursed" label="Exclude Cursed Items" type="checkbox">1</control>
- <control cid="include-PH" label="Include Player's Handbook" type="checkbox">1</control>
- <control cid="include-DM" label="Include DM's Guide" type="checkbox">1</control>
- <control cid="include-XG" label="Include Xanathar's Guide" type="checkbox">1</control>
- <control cid="include-EE" label="Include Elemental Evil" type="checkbox">0</control>
- </div>
- </div>
-
- <br/>
- <br/>
- <button id="go">Generate</button>
- <button id="finalize" style="display:none;">Finalize</button>
- </div>
- <table class="item-table" >
- <thead>
- <tr>
- <th>Item Name</th>
- <th>Rarity</th>
- <th>Qty.<br/>Available</th>
- <th>Price</th>
- <th class="reroll">Reroll</th>
- </tr>
- </thead>
- <tbody class="item-list"></tbody>
- </table>
-
- </body>
- </html>
|