PageSourceSearch

https://topdowntabletop.com/shared/codex-hero-detail.js

js topdowntabletop.com collected 2026-10-08 09:16:22 UTC 15,373 bytes, 371 lines download raw bytes

1/* GENERATED FILE -- do not edit.
2   Emitted from packages/shared/src/codex/hero-detail.ts by `npm run tokens`.
3   Edit the TypeScript source and regenerate; a vitest fails if this drifts. */
4(function () {
5  'use strict';
6
7  var RULES_ANCHOR_RE = /^[a-z0-9-]+$/;
8
9  function isObj(v) {
10    return !!v && typeof v === 'object';
11  }
12
13  function finiteNumber(v) {
14    return typeof v === 'number' && isFinite(v) ? v : null;
15  }
16
17  function isInt(n) {
18    return typeof n === 'number' && isFinite(n) && n === Math.floor(n);
19  }
20
21  function diceList(raw) {
22    var out = [];
23    if (!Array.isArray(raw)) return out;
24    for (var i = 0; i < raw.length; i++) {
25      var d = raw[i];
26      if (!isObj(d) || typeof d.symbol !== 'string' || !d.symbol) continue;
27      var count = typeof d.count === 'number' && isFinite(d.count) && d.count > 0 ? d.count : 1;
28      out.push({ symbol: d.symbol, count: count });
29    }
30    return out;
31  }
32
33  var UPGRADE_LABELS = ['II', 'III', 'IV', 'V'];
34
35  function tierList(raw) {
36    var tiers = [];
37    if (!Array.isArray(raw)) return tiers;
38    for (var i = 0; i < raw.length; i++) {
39      var t = raw[i];
40      if (!isObj(t)) continue;
41      var tn = finiteNumber(t.tier);
42      tiers.push({
43        tier: tn === null ? i + 1 : tn,
44        dice: diceList(t.dice),
45        damage: finiteNumber(t.damage)
46      });
47    }
48    return tiers;
49  }
50
51  function effectList(raw) {
52    var effects = [];
53    if (Array.isArray(raw)) {
54      for (var j = 0; j < raw.length; j++) {
55        var e = raw[j];
56        if (typeof e === 'string' && e.replace(/^\s+|\s+$/g, '')) effects.push(e);
57      }
58    }
59    return effects;
60  }
61
62  function bonusList(raw) {
63    var out = [];
64    if (!Array.isArray(raw)) return out;
65    for (var i = 0; i < raw.length; i++) {
66      var b = raw[i];
67      if (!isObj(b) || typeof b.name !== 'string' || !b.name) continue;
68      var dice = diceList(b.dice);
69      out.push({
70        name: b.name,
71        kind: typeof b.type === 'string' ? b.type.toLowerCase() : '',
72        tiers: dice.length > 0 ? [{ tier: 1, dice: dice, damage: finiteNumber(b.damage) }] : [],
73        effects: effectList(b.effects)
74      });
75    }
76    return out;
77  }
78
79  function upgradeList(raw) {
80    var out = [];
81    if (!Array.isArray(raw)) return out;
82    for (var i = 0; i < raw.length; i++) {
83      var u = raw[i];
84      if (!isObj(u) || typeof u.cardName !== 'string' || !u.cardName) continue;
85      var lv = finiteNumber(u.level);
86      var level = lv === null ? i + 1 : lv;
87      out.push({
88        level: level,
89        label: isInt(level) && level >= 1 && level <= UPGRADE_LABELS.length ? UPGRADE_LABELS[level - 1] : '',
90        cardName: u.cardName,
91        cpCost: finiteNumber(u.cpCost),
92        cardImage: typeof u.cardImage === 'string' && u.cardImage ? u.cardImage : null,
93        tiers: tierList(Array.isArray(u.activation_requirements) ? u.activation_requirements : u.activationRequirements),
94        effects: effectList(u.effects),
95        bonusAbilities: bonusList(u.bonusAbilities)
96      });
97    }
98    return out;
99  }
100
101  var ABILITY_ERRATA = [{"heroSlug":"monk","ability":"Fist of Harmony","effectiveFrom":"2027-01-01","effects":["Gain 2 Chi","Deal 6 dmg"]},{"heroSlug":"monk","ability":"Fist of Harmony","level":1,"effectiveFrom":"2027-01-01","effects":["Gain 3 Chi","Deal 6 dmg"]},{"heroSlug":"monk","ability":"Lotus Strike","effectiveFrom":"2027-01-01","effects":["Increase Chi stack limit by 1, gain 5 Chi","Deal 5 undefendable dmg"]},{"heroSlug":"monk","ability":"Lotus Strike","level":1,"effectiveFrom":"2027-01-01","effects":["Increase Chi stack limit by 1, gain 6 Chi","Deal 6 undefendable dmg"]},{"heroSlug":"monk","ability":"Fist of Tranquility","effectiveFrom":"2027-01-01","effects":["Gain Evasive & 3 Chi","Deal 7 dmg"]},{"heroSlug":"monk","ability":"Fist of Tranquility","level":1,"effectiveFrom":"2027-01-01","effects":["Gain Evasive & 3 Chi","Deal 7 dmg","Inflict Knockdown"]},{"heroSlug":"monk","ability":"Meditate","level":1,"bonus":"Zen Strike","effectiveFrom":"2027-01-01","effects":["Gain 2 chi","Deal 6 dmg"]},{"heroSlug":"monk","ability":"Lotus Strike","level":1,"bonus":"Way Of The Lotus","effectiveFrom":"2027-01-01","effects":["Gain Evasive and 2 Chi","Deal 2 undefendable dmg."]},{"heroSlug":"monk","ability":"Transcendence!","effectiveFrom":"2027-01-01","effects":["Gain Evasive & Cleanse","Inflict Knockdown","Increase Chi stack limit by 1, gain 6 Chi","Deal 10 dmg"]},{"heroSlug":"paladin","ability":"Righteous Prayer","effectiveFrom":"2027-01-01","effects":["Deal 8 dmg","At the end of the Roll Phase, gain Crit and 2 CP"]},{"heroSlug":"paladin","ability":"Righteous Prayer","level":1,"effectiveFrom":"2027-01-01","effects":["Deal 8 undefendable dmg","At the end of the Roll Phase, gain Crit and 2 CP"]},{"heroSlug":"paladin","ability":"Mighty Prayer","effectiveFrom":"2027-01-01","effects":["Gain Crit & Accuracy","Deal 3 undefendable dmg"]},{"heroSlug":"paladin","ability":"Mighty Prayer","level":1,"effectiveFrom":"2027-01-01","effects":["Gain Crit & Accuracy","Deal 4 undefendable dmg"]},{"heroSlug":"spiderman","ability":"Ensnare","effectiveFrom":"2027-01-01","effects":["Small: Deal 5 dmg. At the end of the Roll Phase, inflict Webbed","Large: Draw 1, deal 8 dmg. At the end of the Roll Phase, inflict Webbed"]}];
102
103  function isValidDayKey(key) {
104    if (typeof key !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(key)) return false;
105    var y = Number(key.slice(0, 4));
106    var m = Number(key.slice(5, 7));
107    var d = Number(key.slice(8, 10));
108    if (m < 1 || m > 12 || d < 1) return false;
109    var leap = y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0);
110    var dim = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m - 1];
111    return d <= dim;
112  }
113
114  function errLive(e, todayKey) {
115    return isValidDayKey(todayKey) && todayKey >= e.effectiveFrom;
116  }
117
118  function findErratum(heroSlug, ability, level, bonus) {
119    for (var i = 0; i < ABILITY_ERRATA.length; i++) {
120      var e = ABILITY_ERRATA[i];
121      if (e.heroSlug === heroSlug && e.ability === ability && e.level === level && e.bonus === bonus) {
122        return e;
123      }
124    }
125    return null;
126  }
127
128  function sameList(a, b) {
129    if (!Array.isArray(a) || a.length !== b.length) return false;
130    for (var i = 0; i < b.length; i++) if (a[i] !== b[i]) return false;
131    return true;
132  }
133
134  function copyObj(o, key, value) {
135    var out = {};
136    for (var k in o) if (hasOwn(o, k)) out[k] = o[k];
137    out[key] = value;
138    return out;
139  }
140
141  function upcomingErrata(heroSlug, abilityName, todayKey, level, bonus) {
142    var e = findErratum(heroSlug, abilityName, level, bonus);
143    if (!e || errLive(e, todayKey)) return null;
144    return { effectiveFrom: e.effectiveFrom, effects: e.effects.slice() };
145  }
146
147  function applyErrataToAbility(heroSlug, ability, todayKey) {
148    if (!isObj(ability) || Array.isArray(ability) || typeof ability.name !== 'string') return ability;
149    var name = ability.name;
150    var out = ability;
151    var base = findErratum(heroSlug, name, undefined, undefined);
152    if (base && errLive(base, todayKey) && !sameList(ability.effects, base.effects)) {
153      out = copyObj(out, 'effects', base.effects.slice());
154    }
155    if (Array.isArray(ability.upgrades)) {
156      var changed = false;
157      var upgrades = [];
158      for (var i = 0; i < ability.upgrades.length; i++) {
159        var u = ability.upgrades[i];
160        var nu = u;
161        if (isObj(u) && !Array.isArray(u) && typeof u.level === 'number') {
162          var e = findErratum(heroSlug, name, u.level, undefined);
163          if (e && errLive(e, todayKey) && !sameList(u.effects, e.effects)) {
164            nu = copyObj(nu, 'effects', e.effects.slice());
165          }
166          if (Array.isArray(u.bonusAbilities)) {
167            var bChanged = false;
168            var bonuses = [];
169            for (var j = 0; j < u.bonusAbilities.length; j++) {
170              var b = u.bonusAbilities[j];
171              var nb = b;
172              if (isObj(b) && !Array.isArray(b) && typeof b.name === 'string') {
173                var be = findErratum(heroSlug, name, u.level, b.name);
174                if (be && errLive(be, todayKey) && !sameList(b.effects, be.effects)) {
175                  bChanged = true;
176                  nb = copyObj(b, 'effects', be.effects.slice());
177                }
178              }
179              bonuses.push(nb);
180            }
181            if (bChanged) nu = copyObj(nu, 'bonusAbilities', bonuses);
182          }
183        }
184        if (nu !== u) changed = true;
185        upgrades.push(nu);
186      }
187      if (changed) out = copyObj(out, 'upgrades', upgrades);
188    }
189    return out;
190  }
191
192  var ERRATA_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
193
194  function errataNoteText(upcoming) {
195    var k = upcoming.effectiveFrom;
196    var date = isValidDayKey(k)
197      ? ERRATA_MONTHS[Number(k.slice(5, 7)) - 1] + ' ' + Number(k.slice(8, 10)) + ', ' + k.slice(0, 4)
198      : String(k);
199    return 'Errata from ' + date + ': ' + upcoming.effects.join(' \u00b7 ');
200  }
201
202  function applyAbilityErrata(character, todayKey) {
203    if (!isObj(character) || Array.isArray(character) || !isValidDayKey(todayKey)) return character;
204    var slug = typeof character.id === 'string' ? character.id : character.slug;
205    if (typeof slug !== 'string') return character;
206    var known = false;
207    for (var i = 0; i < ABILITY_ERRATA.length; i++) {
208      if (ABILITY_ERRATA[i].heroSlug === slug) known = true;
209    }
210    if (!known) return character;
211    var out = character;
212    if (Array.isArray(character.abilities)) {
213      var changed = false;
214      var abilities = [];
215      for (var k = 0; k < character.abilities.length; k++) {
216        var a = character.abilities[k];
217        var na = applyErrataToAbility(slug, a, todayKey);
218        if (na !== a) changed = true;
219        abilities.push(na);
220      }
221      if (changed) out = copyObj(out, 'abilities', abilities);
222    }
223    if (isObj(character.ultimate) && !Array.isArray(character.ultimate)) {
224      var nu = applyErrataToAbility(slug, character.ultimate, todayKey);
225      if (nu !== character.ultimate) out = copyObj(out, 'ultimate', nu);
226    }
227    return out;
228  }
229
230  function abilityDetailModel(ability, opts) {
231    var raw = isObj(ability) ? ability : {};
232    var slug = isObj(opts) && typeof opts.heroSlug === 'string' ? opts.heroSlug : '';
233    var key = isObj(opts) && typeof opts.todayKey === 'string' ? opts.todayKey : '';
234    var dated = slug !== '' && key !== '';
235    var a = dated ? applyErrataToAbility(slug, raw, key) : raw;
236    var tiers = tierList(
237      Array.isArray(a.activationRequirements) ? a.activationRequirements : a.activation_requirements
238    );
239    var kind = typeof a.type === 'string' ? a.type.toLowerCase() : '';
240    if (tiers.length === 0 && Array.isArray(a.activation)) {
241      tiers.push({ tier: 1, dice: diceList(a.activation), damage: finiteNumber(a.damage) });
242      if (!kind) kind = 'ultimate';
243    }
244    var model = {
245      name: typeof a.name === 'string' ? a.name : '',
246      kind: kind,
247      tiers: tiers,
248      effects: effectList(a.effects),
249      upgrades: upgradeList(a.upgrades)
250    };
251    if (dated && model.name) {
252      var baseUp = upcomingErrata(slug, model.name, key);
253      if (baseUp) model.upcomingErrata = baseUp;
254      for (var ui = 0; ui < model.upgrades.length; ui++) {
255        var mu = model.upgrades[ui];
256        var upUp = upcomingErrata(slug, model.name, key, mu.level);
257        if (upUp) mu.upcomingErrata = upUp;
258        for (var bi = 0; bi < mu.bonusAbilities.length; bi++) {
259          var mb = mu.bonusAbilities[bi];
260          var bUp = upcomingErrata(slug, model.name, key, mu.level, mb.name);
261          if (bUp) mb.upcomingErrata = bUp;
262        }
263      }
264    }
265    return model;
266  }
267
268  function cardKey(name) {
269    return typeof name === 'string' ? name.replace(/^\s+|\s+$/g, '').toLowerCase() : '';
270  }
271
272  function hasOwn(o, k) {
273    return Object.prototype.hasOwnProperty.call(o, k);
274  }
275
276  function splitHeroCards(cards, manifestTypes, upgradeNames) {
277    var out = { hero: [], upgrade: [], info: [], general: [] };
278    var names = {};
279    if (Array.isArray(upgradeNames)) {
280      for (var n = 0; n < upgradeNames.length; n++) {
281        var u = upgradeNames[n];
282        var nk = cardKey(typeof u === 'string' ? u : isObj(u) ? u.cardName : '');
283        if (nk) names['n:' + nk] = true;
284      }
285    }
286    var types = isObj(manifestTypes) ? manifestTypes : null;
287    var list = Array.isArray(cards) ? cards : [];
288    for (var i = 0; i < list.length; i++) {
289      var card = list[i];
290      if (!isObj(card)) continue;
291      if (card.general === true) {
292        out.general.push(card);
293        continue;
294      }
295      var key = cardKey(card.name);
296      var type = typeof card.type === 'string' ? card.type : '';
297      if (!type && types) {
298        var idx = card.index == null ? '' : String(card.index);
299        var hit;
300        if (idx && hasOwn(types, idx)) hit = types[idx];
301        else if (key && hasOwn(types, key)) hit = types[key];
302        if (typeof hit === 'string') type = hit;
303      }
304      if (type === 'upgrade' || (key && names['n:' + key] === true)) out.upgrade.push(card);
305      else if (type === 'info') out.info.push(card);
306      else out.hero.push(card);
307    }
308    return out;
309  }
310
311  function normalizeToken(e, name) {
312    var raw = e.tokenUrl != null ? e.tokenUrl : e.token_url;
313    if (typeof raw === 'string') return raw ? { url: raw, label: name } : null;
314    if (isObj(raw) && typeof raw.url === 'string' && raw.url) {
315      return { url: raw.url, label: typeof raw.label === 'string' && raw.label ? raw.label : name };
316    }
317    return null;
318  }
319
320  function normalizeRules(raw) {
321    if (!isObj(raw)) return null;
322    if (raw.kind === 'rule') {
323      return typeof raw.anchor === 'string' && RULES_ANCHOR_RE.test(raw.anchor)
324        ? { kind: 'rule', anchor: raw.anchor }
325        : null;
326    }
327    if (raw.kind === 'leaflet') {
328      return isInt(raw.sheet) && raw.sheet >= 0 ? { kind: 'leaflet', sheet: raw.sheet } : null;
329    }
330    return null;
331  }
332
333  function statusEffectsModel(char) {
334    var c = isObj(char) ? char : {};
335    var raw = Array.isArray(c.statusEffects)
336      ? c.statusEffects
337      : Array.isArray(c.status_effects)
338        ? c.status_effects
339        : [];
340    var out = [];
341    for (var i = 0; i < raw.length; i++) {
342      var e = raw[i];
343      if (!isObj(e)) continue;
344      var name = typeof e.name === 'string' ? e.name : '';
345      if (!name) continue;
346      var limit = e.stack_limit != null ? e.stack_limit : e.stackLimit;
347      var textRaw = e.description != null ? e.description : e.text;
348      out.push({
349        name: name,
350        type: typeof e.type === 'string' ? e.type.toLowerCase() : '',
351        stackLabel:
352          typeof limit === 'number' && isFinite(limit) && limit > 0 ? 'Max ' + String(limit) : null,
353        text: typeof textRaw === 'string' ? textRaw : '',
354        token: normalizeToken(e, name),
355        rules: normalizeRules(e.rulesRef)
356      });
357    }
358    return out;
359  }
360
361  window.TDTT = window.TDTT || {};
362  window.TDTT.heroDetail = {
363    abilityDetailModel: abilityDetailModel,
364    applyAbilityErrata: applyAbilityErrata,
365    upcomingErrata: upcomingErrata,
366    errataNoteText: errataNoteText,
367    DT_ABILITY_ERRATA: ABILITY_ERRATA,
368    splitHeroCards: splitHeroCards,
369    statusEffectsModel: statusEffectsModel
370  };
371})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.