// src/functions/summary.js
var data = {
  CHR: [
    { judge: "地狱", grade: 0 },
    { min: 1, judge: "折磨", grade: 0 },
    { min: 2, judge: "不佳", grade: 0 },
    { min: 4, judge: "普通", grade: 0 },
    { min: 7, judge: "优秀", grade: 1 },
    { min: 9, judge: "罕见", grade: 2 },
    { min: 11, judge: "逆天", grade: 3 }
  ],
  MNY: [
    { judge: "地狱", grade: 0 },
    { min: 1, judge: "折磨", grade: 0 },
    { min: 2, judge: "不佳", grade: 0 },
    { min: 4, judge: "普通", grade: 0 },
    { min: 7, judge: "优秀", grade: 1 },
    { min: 9, judge: "罕见", grade: 2 },
    { min: 11, judge: "逆天", grade: 3 }
  ],
  SPR: [
    { judge: "地狱", grade: 0 },
    { min: 1, judge: "折磨", grade: 0 },
    { min: 2, judge: "不幸", grade: 0 },
    { min: 4, judge: "普通", grade: 0 },
    { min: 7, judge: "幸福", grade: 1 },
    { min: 9, judge: "极乐", grade: 2 },
    { min: 11, judge: "天命", grade: 3 }
  ],
  INT: [
    { judge: "地狱", grade: 0 },
    { min: 1, judge: "折磨", grade: 0 },
    { min: 2, judge: "不佳", grade: 0 },
    { min: 4, judge: "普通", grade: 0 },
    { min: 7, judge: "优秀", grade: 1 },
    { min: 9, judge: "罕见", grade: 2 },
    { min: 11, judge: "逆天", grade: 3 },
    { min: 21, judge: "识海", grade: 3 },
    { min: 131, judge: "元神", grade: 3 },
    { min: 501, judge: "仙魂", grade: 3 }
  ],
  STR: [
    { judge: "地狱", grade: 0 },
    { min: 1, judge: "折磨", grade: 0 },
    { min: 2, judge: "不佳", grade: 0 },
    { min: 4, judge: "普通", grade: 0 },
    { min: 7, judge: "优秀", grade: 1 },
    { min: 9, judge: "罕见", grade: 2 },
    { min: 11, judge: "逆天", grade: 3 },
    { min: 21, judge: "凝气", grade: 3 },
    { min: 101, judge: "筑基", grade: 3 },
    { min: 401, judge: "金丹", grade: 3 },
    { min: 1001, judge: "元婴", grade: 3 },
    { min: 2001, judge: "仙体", grade: 3 }
  ],
  AGE: [
    { judge: "胎死腹中", grade: 0 },
    { min: 1, judge: "早夭", grade: 0 },
    { min: 10, judge: "少年", grade: 0 },
    { min: 18, judge: "盛年", grade: 0 },
    { min: 40, judge: "中年", grade: 0 },
    { min: 60, judge: "花甲", grade: 1 },
    { min: 70, judge: "古稀", grade: 1 },
    { min: 80, judge: "杖朝", grade: 2 },
    { min: 90, judge: "南山", grade: 2 },
    { min: 95, judge: "不老", grade: 3 },
    { min: 100, judge: "修仙", grade: 3 },
    { min: 500, judge: "仙寿", grade: 3 }
  ],
  SUM: [
    { judge: "地狱", grade: 0 },
    { min: 41, judge: "折磨", grade: 0 },
    { min: 50, judge: "不佳", grade: 0 },
    { min: 60, judge: "普通", grade: 0 },
    { min: 80, judge: "优秀", grade: 1 },
    { min: 100, judge: "罕见", grade: 2 },
    { min: 110, judge: "逆天", grade: 3 },
    { min: 120, judge: "传说", grade: 3 }
  ]
};
function summary(type, value) {
  let length = data[type].length;
  while (length--) {
    const { min, judge, grade } = data[type][length];
    if (min == undefined || value >= min)
      return { judge, grade };
  }
}

// src/functions/addition.js
function getRate(type, value) {
  switch (type) {
    case "times":
      if (value >= 100)
        return { 2: 6 };
      if (value >= 70)
        return { 2: 5 };
      if (value >= 50)
        return { 2: 4 };
      if (value >= 30)
        return { 2: 3 };
      if (value >= 10)
        return { 2: 2 };
      return { 2: 1 };
    case "achievement":
      if (value >= 100)
        return { 3: 6 };
      if (value >= 70)
        return { 3: 5 };
      if (value >= 50)
        return { 3: 4 };
      if (value >= 30)
        return { 3: 3 };
      if (value >= 10)
        return { 3: 2 };
      return { 3: 1 };
    default:
      return {};
  }
}
function getGrade(type, value) {
  switch (type) {
    case "times":
    case "achievement":
      if (value >= 100)
        return 3;
      if (value >= 50)
        return 2;
      if (value >= 10)
        return 1;
      return 0;
    case "talentRate":
      if (value >= 0.9)
        return 3;
      if (value >= 0.6)
        return 2;
      if (value >= 0.3)
        return 1;
      return 0;
    case "eventRate":
      if (value >= 0.6)
        return 3;
      if (value >= 0.4)
        return 2;
      if (value >= 0.2)
        return 1;
      return 0;
    default:
      return 0;
  }
}

// src/functions/util.js
function clone(value) {
  switch (typeof value) {
    case "object":
      if (Array.isArray(value))
        return value.map((v) => clone(v));
      const newObj = {};
      for (const key in value)
        newObj[key] = clone(value[key]);
      return newObj;
    default:
      return value;
  }
}
function max(...arr) {
  return Math.max(...arr.flat());
}
function min(...arr) {
  return Math.min(...arr.flat());
}
function sum(...arr) {
  let s = 0;
  arr.flat().forEach((v) => s += v);
  return s;
}

// src/property.js
class Property {
  constructor() {}
  TYPES = {
    AGE: "AGE",
    CHR: "CHR",
    INT: "INT",
    STR: "STR",
    MNY: "MNY",
    SPR: "SPR",
    LIF: "LIF",
    TLT: "TLT",
    EVT: "EVT",
    TMS: "TMS",
    LAGE: "LAGE",
    HAGE: "HAGE",
    LCHR: "LCHR",
    HCHR: "HCHR",
    LINT: "LINT",
    HINT: "HINT",
    LSTR: "LSTR",
    HSTR: "HSTR",
    LMNY: "LMNY",
    HMNY: "HMNY",
    LSPR: "LSPR",
    HSPR: "HSPR",
    SUM: "SUM",
    EXT: "EXT",
    ATLT: "ATLT",
    AEVT: "AEVT",
    ACHV: "ACHV",
    CTLT: "RTLT",
    CEVT: "REVT",
    CACHV: "CACHV"
  };
  #ageData;
  #data = {};
  initial({ age }) {
    this.#ageData = age;
    for (const a in age) {
      let { event, talent } = age[a];
      if (!Array.isArray(event))
        event = event?.split(",") || [];
      event = event.map((v) => {
        const value = `${v}`.split("*").map((n) => Number(n));
        if (value.length == 1)
          value.push(1);
        return value;
      });
      if (!Array.isArray(talent))
        talent = talent?.split(",") || [];
      talent = talent.map((v) => Number(v));
      age[a] = { event, talent };
    }
  }
  restart(data2) {
    this.#data = {
      [this.TYPES.AGE]: -1,
      [this.TYPES.CHR]: 0,
      [this.TYPES.INT]: 0,
      [this.TYPES.STR]: 0,
      [this.TYPES.MNY]: 0,
      [this.TYPES.SPR]: 0,
      [this.TYPES.LIF]: 1,
      [this.TYPES.TLT]: [],
      [this.TYPES.EVT]: [],
      [this.TYPES.LAGE]: Infinity,
      [this.TYPES.LCHR]: Infinity,
      [this.TYPES.LINT]: Infinity,
      [this.TYPES.LSTR]: Infinity,
      [this.TYPES.LSPR]: Infinity,
      [this.TYPES.LMNY]: Infinity,
      [this.TYPES.HAGE]: -Infinity,
      [this.TYPES.HCHR]: -Infinity,
      [this.TYPES.HINT]: -Infinity,
      [this.TYPES.HSTR]: -Infinity,
      [this.TYPES.HMNY]: -Infinity,
      [this.TYPES.HSPR]: -Infinity
    };
    for (const key in data2)
      this.change(key, data2[key]);
  }
  restartLastStep() {
    this.#data[this.TYPES.LAGE] = this.get(this.TYPES.AGE);
    this.#data[this.TYPES.LCHR] = this.get(this.TYPES.CHR);
    this.#data[this.TYPES.LINT] = this.get(this.TYPES.INT);
    this.#data[this.TYPES.LSTR] = this.get(this.TYPES.STR);
    this.#data[this.TYPES.LSPR] = this.get(this.TYPES.SPR);
    this.#data[this.TYPES.LMNY] = this.get(this.TYPES.MNY);
    this.#data[this.TYPES.HAGE] = this.get(this.TYPES.AGE);
    this.#data[this.TYPES.HCHR] = this.get(this.TYPES.CHR);
    this.#data[this.TYPES.HINT] = this.get(this.TYPES.INT);
    this.#data[this.TYPES.HSTR] = this.get(this.TYPES.STR);
    this.#data[this.TYPES.HMNY] = this.get(this.TYPES.MNY);
    this.#data[this.TYPES.HSPR] = this.get(this.TYPES.SPR);
  }
  get(prop) {
    switch (prop) {
      case this.TYPES.AGE:
      case this.TYPES.CHR:
      case this.TYPES.INT:
      case this.TYPES.STR:
      case this.TYPES.MNY:
      case this.TYPES.SPR:
      case this.TYPES.LIF:
      case this.TYPES.TLT:
      case this.TYPES.EVT:
        return clone(this.#data[prop]);
      case this.TYPES.LAGE:
      case this.TYPES.LCHR:
      case this.TYPES.LINT:
      case this.TYPES.LSTR:
      case this.TYPES.LMNY:
      case this.TYPES.LSPR:
        return min(this.#data[prop], this.get(this.fallback(prop)));
      case this.TYPES.HAGE:
      case this.TYPES.HCHR:
      case this.TYPES.HINT:
      case this.TYPES.HSTR:
      case this.TYPES.HMNY:
      case this.TYPES.HSPR:
        return max(this.#data[prop], this.get(this.fallback(prop)));
      case this.TYPES.SUM:
        const HAGE = this.get(this.TYPES.HAGE);
        const HCHR = this.get(this.TYPES.HCHR);
        const HINT = this.get(this.TYPES.HINT);
        const HSTR = this.get(this.TYPES.HSTR);
        const HMNY = this.get(this.TYPES.HMNY);
        const HSPR = this.get(this.TYPES.HSPR);
        return Math.floor(sum(HCHR, HINT, HSTR, HMNY, HSPR) * 2 + HAGE / 2);
      case this.TYPES.TMS:
        return this.lsget("times") || 0;
      case this.TYPES.EXT:
        return this.lsget("extendTalent") || null;
      case this.TYPES.ATLT:
      case this.TYPES.AEVT:
      case this.TYPES.ACHV:
        return this.lsget(prop) || [];
      case this.TYPES.CTLT:
      case this.TYPES.CEVT:
      case this.TYPES.CACHV:
        return this.get(this.fallback(prop)).length;
      default:
        return 0;
    }
  }
  fallback(prop) {
    switch (prop) {
      case this.TYPES.LAGE:
      case this.TYPES.HAGE:
        return this.TYPES.AGE;
      case this.TYPES.LCHR:
      case this.TYPES.HCHR:
        return this.TYPES.CHR;
      case this.TYPES.LINT:
      case this.TYPES.HINT:
        return this.TYPES.INT;
      case this.TYPES.LSTR:
      case this.TYPES.HSTR:
        return this.TYPES.STR;
      case this.TYPES.LMNY:
      case this.TYPES.HMNY:
        return this.TYPES.MNY;
      case this.TYPES.LSPR:
      case this.TYPES.HSPR:
        return this.TYPES.SPR;
      case this.TYPES.CTLT:
        return this.TYPES.ATLT;
      case this.TYPES.CEVT:
        return this.TYPES.AEVT;
      case this.TYPES.CACHV:
        return this.TYPES.ACHV;
      default:
        return;
    }
  }
  set(prop, value) {
    switch (prop) {
      case this.TYPES.AGE:
      case this.TYPES.CHR:
      case this.TYPES.INT:
      case this.TYPES.STR:
      case this.TYPES.MNY:
      case this.TYPES.SPR:
      case this.TYPES.LIF:
      case this.TYPES.TLT:
      case this.TYPES.EVT:
        this.hl(prop, this.#data[prop] = clone(value));
        this.achieve(prop, value);
        return;
      case this.TYPES.TMS:
        this.lsset("times", parseInt(value) || 0);
        return;
      case this.TYPES.EXT:
        this.lsset("extendTalent", value);
        return;
      default:
        return;
    }
  }
  getLastRecord() {
    return clone({
      [this.TYPES.AGE]: this.get(this.TYPES.AGE),
      [this.TYPES.CHR]: this.get(this.TYPES.CHR),
      [this.TYPES.INT]: this.get(this.TYPES.INT),
      [this.TYPES.STR]: this.get(this.TYPES.STR),
      [this.TYPES.MNY]: this.get(this.TYPES.MNY),
      [this.TYPES.SPR]: this.get(this.TYPES.SPR)
    });
  }
  change(prop, value) {
    if (Array.isArray(value)) {
      for (const v of value)
        this.change(prop, Number(v));
      return;
    }
    switch (prop) {
      case this.TYPES.AGE:
      case this.TYPES.CHR:
      case this.TYPES.INT:
      case this.TYPES.STR:
      case this.TYPES.MNY:
      case this.TYPES.SPR:
      case this.TYPES.LIF:
        this.hl(prop, this.#data[prop] += Number(value));
        return;
      case this.TYPES.TLT:
      case this.TYPES.EVT:
        const v = this.#data[prop];
        if (value < 0) {
          const index = v.indexOf(value);
          if (index != -1)
            v.splice(index, 1);
        }
        if (!v.includes(value))
          v.push(value);
        this.achieve(prop, value);
        return;
      case this.TYPES.TMS:
        this.set(prop, this.get(prop) + parseInt(value));
        return;
      default:
        return;
    }
  }
  effect(effects) {
    for (const prop in effects)
      this.change(prop, Number(effects[prop]));
  }
  isEnd() {
    return this.get(this.TYPES.LIF) < 1;
  }
  ageNext() {
    this.change(this.TYPES.AGE, 1);
    const age = this.get(this.TYPES.AGE);
    const { event, talent } = this.getAgeData(age);
    return { age, event, talent };
  }
  getAgeData(age) {
    return clone(this.#ageData[age]);
  }
  hl(prop, value) {
    let keys;
    switch (prop) {
      case this.TYPES.AGE:
        keys = [this.TYPES.LAGE, this.TYPES.HAGE];
        break;
      case this.TYPES.CHR:
        keys = [this.TYPES.LCHR, this.TYPES.HCHR];
        break;
      case this.TYPES.INT:
        keys = [this.TYPES.LINT, this.TYPES.HINT];
        break;
      case this.TYPES.STR:
        keys = [this.TYPES.LSTR, this.TYPES.HSTR];
        break;
      case this.TYPES.MNY:
        keys = [this.TYPES.LMNY, this.TYPES.HMNY];
        break;
      case this.TYPES.SPR:
        keys = [this.TYPES.LSPR, this.TYPES.HSPR];
        break;
      default:
        return;
    }
    const [l, h] = keys;
    this.#data[l] = min(this.#data[l], value);
    this.#data[h] = max(this.#data[h], value);
  }
  achieve(prop, newData) {
    let key;
    switch (prop) {
      case this.TYPES.ACHV:
        const lastData2 = this.lsget(prop);
        this.lsset(prop, (lastData2 || []).concat([[newData, Date.now()]]));
        return;
      case this.TYPES.TLT:
        key = this.TYPES.ATLT;
        break;
      case this.TYPES.EVT:
        key = this.TYPES.AEVT;
        break;
      default:
        return;
    }
    const lastData = this.lsget(key) || [];
    this.lsset(key, Array.from(new Set(lastData.concat(newData || []).flat())));
  }
  lsget(key) {
    const data2 = localStorage.getItem(key);
    if (data2 === null)
      return;
    return JSON.parse(data2);
  }
  lsset(key, value) {
    localStorage.setItem(key, JSON.stringify(value));
  }
}
var property_default = Property;

// src/functions/condition.js
function parseCondition(condition) {
  if (!condition)
    return [];
  const conditions = [];
  const length = condition.length;
  const stack = [];
  stack.unshift(conditions);
  let cursor = 0;
  const catchString = (i) => {
    const str = condition.substring(cursor, i).trim();
    cursor = i;
    if (str)
      stack[0].push(str);
  };
  for (let i = 0;i < length; i++) {
    switch (condition[i]) {
      case " ":
        continue;
      case "(":
        catchString(i);
        cursor++;
        const sub = [];
        stack[0].push(sub);
        stack.unshift(sub);
        break;
      case ")":
        catchString(i);
        cursor++;
        stack.shift();
        break;
      case "|":
      case "&":
        catchString(i);
        catchString(i + 1);
        break;
      default:
        continue;
    }
  }
  catchString(length);
  return conditions;
}
function checkCondition(property, condition) {
  if (!condition)
    return true;
  const conditions = parseCondition(condition);
  return checkParsedConditions(property, conditions);
}
function checkParsedConditions(property, conditions) {
  if (!Array.isArray(conditions))
    return checkProp(property, conditions);
  if (conditions.length == 0)
    return true;
  if (conditions.length == 1)
    return checkParsedConditions(property, conditions[0]);
  let ret = checkParsedConditions(property, conditions[0]);
  for (let i = 1;i < conditions.length; i += 2) {
    switch (conditions[i]) {
      case "&":
        if (ret)
          ret = checkParsedConditions(property, conditions[i + 1]);
        break;
      case "|":
        if (ret)
          return true;
        ret = checkParsedConditions(property, conditions[i + 1]);
        break;
      default:
        return false;
    }
  }
  return ret;
}
function checkProp(property, condition) {
  const length = condition.length;
  let i = condition.search(/[><\!\?=]/);
  const prop = condition.substring(0, i);
  const symbol = condition.substring(i, i += condition[i + 1] == "=" ? 2 : 1);
  const d = condition.substring(i, length);
  const propData = property.get(prop);
  const conditionData = d[0] == "[" ? JSON.parse(d) : Number(d);
  switch (symbol) {
    case ">":
      return propData > conditionData;
    case "<":
      return propData < conditionData;
    case ">=":
      return propData >= conditionData;
    case "<=":
      return propData <= conditionData;
    case "=":
      if (Array.isArray(propData))
        return propData.includes(conditionData);
      return propData == conditionData;
    case "!=":
      if (Array.isArray(propData))
        return !propData.includes(conditionData);
      return propData != conditionData;
    case "?":
      if (Array.isArray(propData)) {
        for (const p of propData)
          if (conditionData.includes(p))
            return true;
        return false;
      }
      return conditionData.includes(propData);
    case "!":
      if (Array.isArray(propData)) {
        for (const p of propData)
          if (conditionData.includes(p))
            return false;
        return true;
      }
      return !conditionData.includes(propData);
    default:
      return false;
  }
}
function extractMaxTriggers(condition) {
  if (!condition)
    return 1;
  const RE_AGE_CONDITION = /AGE\?\[([0-9\,]+)\]/;
  const match_object = RE_AGE_CONDITION.exec(condition);
  if (match_object == null) {
    return 1;
  }
  const age_list = match_object[1].split(",");
  return age_list.length;
}

// src/event.js
class Event {
  constructor() {}
  #events;
  initial({ events }) {
    this.#events = events;
    for (const id in events) {
      const event = events[id];
      if (!event.branch)
        continue;
      event.branch = event.branch.map((b) => {
        b = b.split(":");
        b[1] = Number(b[1]);
        return b;
      });
    }
  }
  count() {
    return Object.keys(this.#events).length;
  }
  check(eventId, property) {
    const { include, exclude, NoRandom } = this.get(eventId);
    if (NoRandom)
      return false;
    if (exclude && checkCondition(property, exclude))
      return false;
    if (include)
      return checkCondition(property, include);
    return true;
  }
  get(eventId) {
    const event = this.#events[eventId];
    if (!event)
      throw new Error(`[ERROR] No Event[${eventId}]`);
    return clone(event);
  }
  information(eventId) {
    const { event: description } = this.get(eventId);
    return { description };
  }
  do(eventId, property) {
    const { effect, branch, event: description, postEvent } = this.get(eventId);
    if (branch) {
      for (const [cond, next] of branch)
        if (checkCondition(property, cond))
          return { effect, next, description };
    }
    return { effect, postEvent, description };
  }
}
var event_default = Event;

// src/talent.js
class Talent {
  constructor() {}
  #talents;
  initial({ talents }) {
    this.#talents = talents;
    for (const id in talents) {
      const talent = talents[id];
      talent.id = Number(id);
      talent.grade = Number(talent.grade);
      talent.max_triggers = extractMaxTriggers(talent.condition);
    }
  }
  count() {
    return Object.keys(this.#talents).length;
  }
  check(talentId, property) {
    const { condition } = this.get(talentId);
    return checkCondition(property, condition);
  }
  get(talentId) {
    const talent = this.#talents[talentId];
    if (!talent)
      throw new Error(`[ERROR] No Talent[${talentId}]`);
    return clone(talent);
  }
  information(talentId) {
    const { grade, name, description } = this.get(talentId);
    return { grade, name, description };
  }
  exclusive(talends, exclusiveId) {
    const { exclusive } = this.get(exclusiveId);
    if (!exclusive)
      return null;
    for (const talent of talends) {
      for (const e of exclusive) {
        if (talent == e)
          return talent;
      }
    }
    return null;
  }
  talentRandom(include, { times = 0, achievement = 0 } = {}) {
    const rate = { 1: 100, 2: 10, 3: 1 };
    const rateAddition = { 1: 1, 2: 1, 3: 1 };
    const timesRate = getRate("times", times);
    const achievementRate = getRate("achievement", achievement);
    for (const grade in timesRate)
      rateAddition[grade] += timesRate[grade] - 1;
    for (const grade in achievementRate)
      rateAddition[grade] += achievementRate[grade] - 1;
    for (const grade in rateAddition)
      rate[grade] *= rateAddition[grade];
    const randomGrade = () => {
      let randomNumber = Math.floor(Math.random() * 1000);
      if ((randomNumber -= rate[3]) < 0)
        return 3;
      if ((randomNumber -= rate[2]) < 0)
        return 2;
      if ((randomNumber -= rate[1]) < 0)
        return 1;
      return 0;
    };
    const talentList = {};
    for (const talentId in this.#talents) {
      const { id, grade, name, description } = this.#talents[talentId];
      if (id == include) {
        include = { grade, name, description, id };
        continue;
      }
      if (!talentList[grade])
        talentList[grade] = [{ grade, name, description, id }];
      else
        talentList[grade].push({ grade, name, description, id });
    }
    return new Array(10).fill(1).map((v, i) => {
      if (!i && include)
        return include;
      let grade = randomGrade();
      while (talentList[grade].length == 0)
        grade--;
      const length = talentList[grade].length;
      const random = Math.floor(Math.random() * length) % length;
      return talentList[grade].splice(random, 1)[0];
    });
  }
  allocationAddition(talents) {
    if (Array.isArray(talents)) {
      let addition = 0;
      for (const talent of talents)
        addition += this.allocationAddition(talent);
      return addition;
    }
    return Number(this.get(talents).status) || 0;
  }
  do(talentId, property) {
    const { effect, condition, grade, name, description } = this.get(talentId);
    if (condition && !checkCondition(property, condition))
      return null;
    return { effect, grade, name, description };
  }
}
var talent_default = Talent;

// src/achievement.js
class Achievement {
  constructor() {}
  Opportunity = {
    START: "START",
    TRAJECTORY: "TRAJECTORY",
    SUMMARY: "SUMMARY",
    END: "END"
  };
  #achievements;
  initial({ achievements }) {
    this.#achievements = achievements;
  }
  count() {
    return Object.keys(this.#achievements).length;
  }
  list(property) {
    return Object.values(this.#achievements).map(({
      id,
      name,
      opportunity,
      description,
      hide,
      grade
    }) => ({
      id,
      name,
      opportunity,
      description,
      hide,
      grade,
      isAchieved: this.isAchieved(id, property)
    }));
  }
  get(achievementId) {
    const achievement = this.#achievements[achievementId];
    if (!achievement)
      throw new Error(`[ERROR] No Achievement[${achievementId}]`);
    return clone(achievement);
  }
  check(achievementId, property) {
    const { condition } = this.get(achievementId);
    return checkCondition(property, condition);
  }
  isAchieved(achievementId, property) {
    for (const [achieved] of property.get(property.TYPES.ACHV) || [])
      if (achieved == achievementId)
        return true;
    return false;
  }
  achieve(opportunity, property) {
    this.list(property).filter(({ isAchieved }) => !isAchieved).filter(({ opportunity: o }) => o == opportunity).filter(({ id }) => this.check(id, property)).forEach(({ id }) => {
      property.achieve(property.TYPES.ACHV, id);
      $$event("achievement", this.get(id));
    });
  }
}
var achievement_default = Achievement;

// src/life.js
class Life {
  constructor() {
    this.#property = new property_default;
    this.#event = new event_default;
    this.#talent = new talent_default;
    this.#achievement = new achievement_default;
  }
  #property;
  #event;
  #talent;
  #achievement;
  #triggerTalents;
  async initial() {
    const [age, talents, events, achievements] = await Promise.all([
      json("age"),
      json("talents"),
      json("events"),
      json("achievement")
    ]);
    this.#property.initial({ age });
    this.#talent.initial({ talents });
    this.#event.initial({ events });
    this.#achievement.initial({ achievements });
  }
  restart(allocation) {
    this.#triggerTalents = {};
    this.#property.restart(allocation);
    this.doTalent();
    this.#property.restartLastStep();
    this.#achievement.achieve(this.#achievement.Opportunity.START, this.#property);
  }
  getTalentAllocationAddition(talents) {
    return this.#talent.allocationAddition(talents);
  }
  getTalentCurrentTriggerCount(talentId) {
    return this.#triggerTalents[talentId] || 0;
  }
  next() {
    const { age, event, talent } = this.#property.ageNext();
    const talentContent = this.doTalent(talent);
    const eventContent = this.doEvent(this.random(event));
    const isEnd = this.#property.isEnd();
    const content = [talentContent, eventContent].flat();
    this.#achievement.achieve(this.#achievement.Opportunity.TRAJECTORY, this.#property);
    return { age, content, isEnd };
  }
  doTalent(talents) {
    if (talents)
      this.#property.change(this.#property.TYPES.TLT, talents);
    talents = this.#property.get(this.#property.TYPES.TLT).filter((talentId) => this.getTalentCurrentTriggerCount(talentId) < this.#talent.get(talentId).max_triggers);
    const contents = [];
    for (const talentId of talents) {
      const result = this.#talent.do(talentId, this.#property);
      if (!result)
        continue;
      this.#triggerTalents[talentId] = this.getTalentCurrentTriggerCount(talentId) + 1;
      const { effect, name, description, grade } = result;
      contents.push({
        type: this.#property.TYPES.TLT,
        name,
        grade,
        description
      });
      if (!effect)
        continue;
      this.#property.effect(effect);
    }
    return contents;
  }
  doEvent(eventId) {
    if (eventId == null)
      return [];
    const { effect, next, description, postEvent } = this.#event.do(eventId, this.#property);
    this.#property.change(this.#property.TYPES.EVT, eventId);
    this.#property.effect(effect);
    const content = {
      type: this.#property.TYPES.EVT,
      description,
      postEvent
    };
    if (next)
      return [content, this.doEvent(next)].flat();
    return [content];
  }
  random(events) {
    if (!Array.isArray(events) || events.length === 0)
      return null;
    events = events.filter(([eventId]) => this.#event.check(eventId, this.#property));
    if (events.length === 0)
      return null;
    let totalWeights = 0;
    for (const [, weight] of events)
      totalWeights += weight;
    let random = Math.random() * totalWeights;
    for (const [eventId, weight] of events)
      if ((random -= weight) < 0)
        return eventId;
    return events[events.length - 1];
  }
  talentRandom() {
    const times = this.#property.get(this.#property.TYPES.TMS);
    const achievement = this.#property.get(this.#property.TYPES.CACHV);
    return this.#talent.talentRandom(this.getLastExtendTalent(), { times, achievement });
  }
  talentExtend(talentId) {
    this.#property.set(this.#property.TYPES.EXT, talentId);
  }
  getLastExtendTalent() {
    return this.#property.get(this.#property.TYPES.EXT);
  }
  getSummary() {
    this.#achievement.achieve(this.#achievement.Opportunity.SUMMARY, this.#property);
    return {
      AGE: this.#property.get(this.#property.TYPES.HAGE),
      CHR: this.#property.get(this.#property.TYPES.HCHR),
      INT: this.#property.get(this.#property.TYPES.HINT),
      STR: this.#property.get(this.#property.TYPES.HSTR),
      MNY: this.#property.get(this.#property.TYPES.HMNY),
      SPR: this.#property.get(this.#property.TYPES.HSPR),
      SUM: this.#property.get(this.#property.TYPES.SUM)
    };
  }
  getLastRecord() {
    return this.#property.getLastRecord();
  }
  exclusive(talents, exclusive) {
    return this.#talent.exclusive(talents, exclusive);
  }
  getAchievements() {
    const ticks = {};
    this.#property.get(this.#property.TYPES.ACHV).forEach(([id, tick]) => ticks[id] = tick);
    return this.#achievement.list(this.#property).sort(({ id: a, grade: ag, hide: ah }, { id: b, grade: bg, hide: bh }) => {
      a = ticks[a];
      b = ticks[b];
      if (a && b)
        return b - a;
      if (!a && !b) {
        if (ah && bh)
          return bg - ag;
        if (ah)
          return 1;
        if (bh)
          return -1;
        return bg - ag;
      }
      if (!a)
        return 1;
      if (!b)
        return -1;
    });
  }
  getTotal() {
    const TMS = this.#property.get(this.#property.TYPES.TMS);
    const CACHV = this.#property.get(this.#property.TYPES.CACHV);
    const CTLT = this.#property.get(this.#property.TYPES.CTLT);
    const CEVT = this.#property.get(this.#property.TYPES.CEVT);
    const totalTalent = this.#talent.count();
    const totalEvent = this.#event.count();
    return {
      times: TMS,
      achievement: CACHV,
      talentRate: CTLT / totalTalent,
      eventRate: CEVT / totalEvent
    };
  }
  get times() {
    return this.#property?.get(this.#property.TYPES.TMS) || 0;
  }
  set times(v) {
    this.#property?.set(this.#property.TYPES.TMS, v);
    this.#achievement.achieve(this.#achievement.Opportunity.END, this.#property);
  }
}
var life_default = Life;

// src/app.js
class App {
  constructor() {
    this.#life = new life_default;
  }
  #life;
  #pages;
  #currentPage;
  #talentSelected = new Set;
  #totalMax = 20;
  #isEnd = false;
  #selectedExtendTalent = null;
  #hintTimeout;
  #specialthanks;
  #autoTrajectory;
  stopAutoTrajectory() {
    if (!this.#autoTrajectory)
      return;
    clearInterval(this.#autoTrajectory);
    this.#autoTrajectory = null;
  }
  async initial() {
    this.initPages();
    this.switch("loading");
    const [, specialthanks] = await Promise.all([
      this.#life.initial(),
      json("specialthanks")
    ]);
    this.#specialthanks = specialthanks;
    this.switch("index");
    globalThis.onerror = (event, source, lineno, colno, error) => {
      this.hint(`[ERROR] at (${source}:${lineno}:${colno})

${error?.stack || error || "unknow Error"}`, "error");
    };
    const keyDownCallback = (keyboardEvent) => {
      if (keyboardEvent.which === 13 || keyboardEvent.keyCode === 13) {
        const pressEnterFunc = this.#pages[this.#currentPage]?.pressEnter;
        pressEnterFunc && typeof pressEnterFunc === "function" && pressEnterFunc();
      }
    };
    globalThis.removeEventListener("keydown", keyDownCallback);
    globalThis.addEventListener("keydown", keyDownCallback);
  }
  initPages() {
    const loadingPage = $(`
        <div id="main">
            <div id="title">
                人生重开模拟器<br>
                <div style="font-size:1.5rem; font-weight:normal;">加载中...</div>
            </div>
        </div>
        `);
    const indexPage = $(`
        <div id="main">
            <button id="achievement">成就</button>
            <button id="specialthanks">特别感谢</button>
            <button id="themeToggleBtn">黑</button>
            <div id="title">
                人生重开模拟器<br>
                <div style="font-size:1.5rem; font-weight:normal;">这垃圾人生一秒也不想呆了</div>
            </div>
            <button id="restart" class="mainbtn"><span class="iconfont">&#xe6a7;</span>立即重开</button>
        </div>
        `);
    this.setTheme(localStorage.getItem("theme"));
    indexPage.find("#restart").click(() => this.switch("talent"));
    indexPage.find("#achievement").click(() => this.switch("achievement"));
    if (localStorage.getItem("theme") == "light") {
      indexPage.find("#themeToggleBtn").text("黑");
    } else {
      indexPage.find("#themeToggleBtn").text("白");
    }
    indexPage.find("#themeToggleBtn").click(() => {
      if (localStorage.getItem("theme") == "light") {
        localStorage.setItem("theme", "dark");
        indexPage.find("#themeToggleBtn").text("白");
      } else {
        localStorage.setItem("theme", "light");
        indexPage.find("#themeToggleBtn").text("黑");
      }
      this.setTheme(localStorage.getItem("theme"));
    });
    indexPage.find("#specialthanks").click(() => this.switch("specialthanks"));
    const specialThanksPage = $(`
        <div id="main">
            <button id="specialthanks">返回</button>
            <div id="spthx">
                <ul class="g1"></ul>
                <ul class="g2"></ul>
            </div>
        </div>
        `);
    specialThanksPage.find("#specialthanks").click(() => this.switch("index"));
    const achievementPage = $(`
        <div id="main">
            <button id="specialthanks">返回</button>
            <span class="title">统计</span>
            <ul id="total"></ul>
            <span style="padding:0.25rem; margin: 0.5rem 0; border: none; background: #ccc;"></span>
            <span class="title">成就</span>
            <ul id="achievements"></ul>
        `);
    achievementPage.find("#specialthanks").click(() => this.switch("index"));
    const talentPage = $(`
        <div id="main">
            <div class="head" style="font-size: 1.6rem">天赋抽卡</div>
            <button id="random" class="mainbtn" style="position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);"">10连抽！</button>
            <ul id="talents" class="selectlist"></ul>
            <button id="next" class="mainbtn">请选择3个</button>
        </div>
        `);
    const createTalent = ({ grade, name, description }) => {
      return $(`<li class="grade${grade}b">${name}（${description}）</li>`);
    };
    talentPage.find("#random").click(() => {
      talentPage.find("#random").hide();
      const ul2 = talentPage.find("#talents");
      this.#life.talentRandom().forEach((talent) => {
        const li = createTalent(talent);
        ul2.append(li);
        li.click(() => {
          if (li.hasClass("selected")) {
            li.removeClass("selected");
            this.#talentSelected.delete(talent);
            if (this.#talentSelected.size < 3) {
              talentPage.find("#next").text("请选择3个");
            }
          } else {
            if (this.#talentSelected.size == 3) {
              this.hint("只能选3个天赋");
              return;
            }
            const exclusive = this.#life.exclusive(Array.from(this.#talentSelected).map(({ id }) => id), talent.id);
            if (exclusive != null) {
              for (const { name, id } of this.#talentSelected) {
                if (id == exclusive) {
                  this.hint(`与已选择的天赋【${name}】冲突`);
                  return;
                }
              }
              return;
            }
            li.addClass("selected");
            this.#talentSelected.add(talent);
            if (this.#talentSelected.size == 3) {
              talentPage.find("#next").text("开始新人生");
            }
          }
        });
      });
      talentPage.find("#next").show();
    });
    talentPage.find("#next").click(() => {
      if (this.#talentSelected.size != 3) {
        this.hint("请选择3个天赋");
        return;
      }
      talentPage.find("#next").hide();
      this.#totalMax = 20 + this.#life.getTalentAllocationAddition(Array.from(this.#talentSelected).map(({ id }) => id));
      this.switch("property");
    });
    const propertyPage = $(`
        <div id="main">
            <div class="head" style="font-size: 1.6rem">
                <div>调整初始属性</div>
                <div id="total" style="font-size:1rem; font-weight:normal;">可用属性点：0</div>
            </div>
            <ul id="propertyAllocation" class="propinitial"></ul>
            <ul class="selectlist" id="talentSelectedView"></ul>
            <div class="btn-area">
                <button id="random" class="mainbtn">随机分配</button>
                <button id="start" class="mainbtn">开始新人生</button>
            </div>
        </div>
        `);
    propertyPage.mounted = () => {
      propertyPage.find("#talentSelectedView").append(`<li>已选天赋</li>` + Array.from(this.#talentSelected).map(({ name, description }) => `<li class="grade0b">${name}(${description})</li>`).join(""));
    };
    const groups = {};
    const total = () => {
      let t = 0;
      for (const type in groups)
        t += groups[type].get();
      return t;
    };
    const freshTotal = () => {
      propertyPage.find("#total").text(`可用属性点：${this.#totalMax - total()}`);
    };
    const getBtnGroups = (name, min2, max2) => {
      const group = $(`<li>${name}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</li>`);
      const btnSub = $(`<span class="iconfont propbtn">&#xe6a5;</span>`);
      const inputBox = $(`<input value="0">`);
      const btnAdd = $(`<span class="iconfont propbtn">&#xe6a6;</span>`);
      group.append(btnSub);
      group.append(inputBox);
      group.append(btnAdd);
      const limit = (v) => {
        v = Number(v) || 0;
        v = Math.round(v);
        return v < min2 ? min2 : v > max2 ? max2 : v;
      };
      const get = () => Number(inputBox.val());
      const set = (v) => {
        inputBox.val(limit(v));
        freshTotal();
      };
      btnAdd.click(() => {
        if (total() >= this.#totalMax) {
          this.hint("没有可分配的点数了");
          return;
        }
        set(get() + 1);
      });
      btnSub.click(() => set(get() - 1));
      inputBox.on("input", () => {
        const t = total();
        let val = get();
        if (t > this.#totalMax) {
          val -= t - this.#totalMax;
        }
        val = limit(val);
        if (val != inputBox.val()) {
          set(val);
        }
        freshTotal();
      });
      return { group, get, set };
    };
    groups.CHR = getBtnGroups("颜值", 0, 10);
    groups.INT = getBtnGroups("智力", 0, 10);
    groups.STR = getBtnGroups("体质", 0, 10);
    groups.MNY = getBtnGroups("家境", 0, 10);
    const ul = propertyPage.find("#propertyAllocation");
    for (const type in groups) {
      ul.append(groups[type].group);
    }
    propertyPage.find("#random").click(() => {
      let t = this.#totalMax;
      const arr = [10, 10, 10, 10];
      while (t > 0) {
        const sub = Math.round(Math.random() * (Math.min(t, 10) - 1)) + 1;
        while (true) {
          const select = Math.floor(Math.random() * 4) % 4;
          if (arr[select] - sub < 0)
            continue;
          arr[select] -= sub;
          t -= sub;
          break;
        }
      }
      groups.CHR.set(10 - arr[0]);
      groups.INT.set(10 - arr[1]);
      groups.STR.set(10 - arr[2]);
      groups.MNY.set(10 - arr[3]);
    });
    propertyPage.find("#start").click(() => {
      if (total() < this.#totalMax) {
        this.hint(`你还有${this.#totalMax - total()}属性点没有分配完`);
        return;
      } else if (total() > this.#totalMax) {
        this.hint(`你多使用了${total() - this.#totalMax}属性点`);
        return;
      }
      this.#life.restart({
        CHR: groups.CHR.get(),
        INT: groups.INT.get(),
        STR: groups.STR.get(),
        MNY: groups.MNY.get(),
        SPR: 5,
        TLT: Array.from(this.#talentSelected).map(({ id }) => id)
      });
      this.switch("trajectory");
      this.#pages.trajectory.born();
    });
    const trajectoryPage = $(`
        <div id="main">
            <ul id="lifeProperty" class="lifeProperty"></ul>
            <ul id="lifeTrajectory" class="lifeTrajectory"></ul>
            <div class="btn-area">
                <button id="auto" class="mainbtn">自动播放</button>
                <button id="auto2x" class="mainbtn">自动播放2x</button>
                <button id="summary" class="mainbtn">人生总结</button>
                <button id="domToImage" class="mainbtn">人生回放</button>
            </div>
            <div class="domToImage2wx">
                <img src="" id="endImage" />
            </div>
        </div>
        `);
    trajectoryPage.find("#lifeTrajectory").click(() => {
      if (this.#isEnd)
        return;
      const trajectory = this.#life.next();
      const { age, content, isEnd } = trajectory;
      const li = $(`<li><span>${age}岁：</span><span>${content.map(({ type, description, grade, name, postEvent }) => {
        switch (type) {
          case "TLT":
            return `天赋【${name}】发动：${description}`;
          case "EVT":
            return description + (postEvent ? `<br>${postEvent}` : "");
        }
      }).join("<br>")}</span></li>`);
      li.appendTo("#lifeTrajectory");
      $("#lifeTrajectory").scrollTop($("#lifeTrajectory")[0].scrollHeight);
      if (isEnd) {
        $(document).unbind("keydown");
        this.#isEnd = true;
        trajectoryPage.find("#summary").show();
        trajectoryPage.find("#auto").hide();
        trajectoryPage.find("#auto2x").hide();
      } else {
        const property = this.#life.getLastRecord();
        $("#lifeProperty").html(`
                    <li><span>颜值</span><span>${property.CHR}</span></li>
                    <li><span>智力</span><span>${property.INT}</span></li>
                    <li><span>体质</span><span>${property.STR}</span></li>
                    <li><span>家境</span><span>${property.MNY}</span></li>
                    <li><span>快乐</span><span>${property.SPR}</span></li>
                    `);
      }
    });
    trajectoryPage.find("#domToImage").click(() => {
      $("#lifeTrajectory").addClass("deleteFixed");
      const ua = navigator.userAgent.toLowerCase();
      domtoimage.toJpeg(document.getElementById("lifeTrajectory")).then(function(dataUrl) {
        let link = document.createElement("a");
        link.download = "我的人生回放.jpeg";
        link.href = dataUrl;
        link.click();
        $("#lifeTrajectory").removeClass("deleteFixed");
        if (ua.match(/MicroMessenger/i) == "micromessenger") {
          $("#endImage").attr("src", dataUrl);
        }
      });
    }).hide();
    trajectoryPage.find("#summary").click(() => {
      this.stopAutoTrajectory();
      this.switch("summary");
    });
    const auto = (tick) => {
      if (this.#autoTrajectory) {
        this.stopAutoTrajectory();
      } else {
        if (!this.#isEnd)
          trajectoryPage.find("#lifeTrajectory").click();
        this.#autoTrajectory = setInterval(() => {
          if (this.#isEnd) {
            this.stopAutoTrajectory();
          } else {
            trajectoryPage.find("#lifeTrajectory").click();
          }
        }, tick);
      }
    };
    trajectoryPage.find("#auto").click(() => auto(1000));
    trajectoryPage.find("#auto2x").click(() => auto(500));
    const summaryPage = $(`
        <div id="main">
            <div class="head">人生总结</div>
            <ul id="judge" class="judge">
                <li class="grade2"><span>颜值：</span><span>9级 美若天仙</span></li>
                <li class="grade0"><span>智力：</span><span>4级 智力一般</span></li>
                <li class="grade0"><span>体质：</span><span>1级 极度虚弱</span></li>
                <li class="grade0"><span>家境：</span><span>6级 小康之家</span></li>
                <li class="grade0"><span>享年：</span><span>3岁 早夭</span></li>
                <li class="grade0"><span>快乐：</span><span></span>3级 不太幸福的人生</li>
            </ul>
            <div class="head" style="height:auto;">天赋，你可以选一个，下辈子还能抽到</div>
            <ul id="talents" class="selectlist" style="flex: 0 1 auto;">
                <li class="grade2b">黑幕（面试一定成功）</li>
            </ul>
            <button id="again" class="mainbtn"><span class="iconfont">&#xe6a7;</span>再次重开</button>
        </div>
        `);
    summaryPage.find("#again").click(() => {
      this.times++;
      this.#life.talentExtend(this.#selectedExtendTalent);
      this.#selectedExtendTalent = null;
      this.#talentSelected.clear();
      this.#totalMax = 20;
      this.#isEnd = false;
      this.switch("index");
    });
    this.#pages = {
      loading: {
        page: loadingPage,
        clear: () => {
          this.#currentPage = "loading";
        }
      },
      index: {
        page: indexPage,
        btnAchievement: indexPage.find("#achievement"),
        btnRestart: indexPage.find("#restart"),
        hint: indexPage.find(".hint"),
        pressEnter: () => {
          this.#pages.index.btnRestart.click();
        },
        clear: () => {
          this.#currentPage = "index";
          indexPage.find(".hint").hide();
          const times = this.times;
          const achievement = indexPage.find("#achievement");
          const specialthanks = indexPage.find("#specialthanks");
          if (times > 0) {
            achievement.show();
            specialthanks.show();
            return;
          }
          achievement.hide();
          specialthanks.hide();
        }
      },
      specialthanks: {
        page: specialThanksPage,
        clear: () => {
          const groups2 = [
            specialThanksPage.find("#spthx > ul.g1"),
            specialThanksPage.find("#spthx > ul.g2")
          ];
          groups2.forEach((g) => g.empty());
          this.#specialthanks.sort(() => 0.5 - Math.random()).forEach(({ group, name, comment, color }) => groups2[--group].append(`
                            <li>
                                <span class="name" ${color ? 'style="color:' + color + '"' : ""}>${name}</span>
                                <span class="comment">${comment || ""}</span>
                            </li>
                        `));
        }
      },
      achievement: {
        page: achievementPage,
        clear: () => {
          const total2 = achievementPage.find("ul#total");
          const achievements = achievementPage.find("ul#achievements");
          total2.empty();
          achievements.empty();
          const formatRate = (type, value) => {
            const rate = getRate(type, value);
            let color = Object.keys(rate)[0];
            switch (parseInt(color)) {
              case 0:
                color = "白色";
                break;
              case 1:
                color = "蓝色";
                break;
              case 2:
                color = "紫色";
                break;
              case 3:
                color = "橙色";
                break;
              default:
                break;
            }
            let r = Object.values(rate)[0];
            switch (parseInt(r)) {
              case 1:
                r = "不变";
                break;
              case 2:
                r = "翻倍";
                break;
              case 3:
                r = "三倍";
                break;
              case 4:
                r = "四倍";
                break;
              case 5:
                r = "五倍";
                break;
              case 6:
                r = "六倍";
                break;
              default:
                break;
            }
            return `抽到${color}概率${r}`;
          };
          const { times, achievement, talentRate, eventRate } = this.#life.getTotal();
          total2.append(`
                        <li class="achvg${getGrade("times", times)}"><span class="achievementtitle">已重开${times}次</span>${formatRate("times", times)}</li>
                        <li class="achvg${getGrade("achievement", achievement)}"><span class="achievementtitle">成就达成${achievement}个</span>${formatRate("achievement", achievement)}</li>
                        <li class="achvg${getGrade("eventRate", eventRate)}"><span class="achievementtitle">事件收集率</span>${Math.floor(eventRate * 100)}%</li>
                        <li class="achvg${getGrade("talentRate", talentRate)}"><span class="achievementtitle">天赋收集率</span>${Math.floor(talentRate * 100)}%</li>
                    `);
          const achievementsData = this.#life.getAchievements();
          achievementsData.forEach(({
            name,
            description,
            hide,
            grade,
            isAchieved
          }) => {
            if (hide && !isAchieved)
              name = description = "???";
            achievements.append(`<li class="achvg${grade} ${isAchieved ? "" : "mask"}"><span class="achievementtitle">${name}</span>${description}</li>`);
          });
        }
      },
      talent: {
        page: talentPage,
        talentList: talentPage.find("#talents"),
        btnRandom: talentPage.find("#random"),
        btnNext: talentPage.find("#next"),
        pressEnter: () => {
          const talentList = this.#pages.talent.talentList;
          const btnRandom = this.#pages.talent.btnRandom;
          const btnNext = this.#pages.talent.btnNext;
          if (talentList.children().length) {
            btnNext.click();
          } else {
            btnRandom.click();
          }
        },
        clear: () => {
          this.#currentPage = "talent";
          talentPage.find("ul.selectlist").empty();
          talentPage.find("#random").show();
          this.#totalMax = 20;
        }
      },
      property: {
        page: propertyPage,
        btnStart: propertyPage.find("#start"),
        pressEnter: () => {
          this.#pages.property.btnStart.click();
        },
        clear: () => {
          this.#currentPage = "property";
          freshTotal();
          propertyPage.find("#talentSelectedView").empty();
        }
      },
      trajectory: {
        page: trajectoryPage,
        lifeTrajectory: trajectoryPage.find("#lifeTrajectory"),
        pressEnter: () => {
          this.#pages.trajectory.lifeTrajectory.click();
        },
        clear: () => {
          this.#currentPage = "trajectory";
          this.stopAutoTrajectory();
          trajectoryPage.find("#lifeTrajectory").empty();
          trajectoryPage.find("#summary").hide();
          trajectoryPage.find("#auto").show();
          trajectoryPage.find("#auto2x").show();
          this.#isEnd = false;
        },
        born: () => {
          trajectoryPage.find("#lifeTrajectory").trigger("click");
        }
      },
      summary: {
        page: summaryPage,
        clear: () => {
          this.#currentPage = "summary";
          const judge = summaryPage.find("#judge");
          const talents = summaryPage.find("#talents");
          judge.empty();
          talents.empty();
          const lastExtendTalent = this.#life.getLastExtendTalent();
          Array.from(this.#talentSelected).sort(({ id: a, grade: ag }, { id: b, grade: bg }) => {
            if (a == lastExtendTalent)
              return -1;
            if (b == lastExtendTalent)
              return 1;
            return bg - ag;
          }).forEach((talent, i) => {
            const li = createTalent(talent);
            talents.append(li);
            li.click(() => {
              if (li.hasClass("selected")) {
                this.#selectedExtendTalent = null;
                li.removeClass("selected");
              } else if (this.#selectedExtendTalent != null) {
                this.hint("只能继承一个天赋");
                return;
              } else {
                this.#selectedExtendTalent = talent.id;
                li.addClass("selected");
              }
            });
            if (!i)
              li.click();
          });
          const summaryData = this.#life.getSummary();
          const format = (discription, type) => {
            const value = summaryData[type];
            const { judge: judge2, grade } = summary(type, value);
            return `<li class="grade${grade}"><span>${discription}：</span><span>${value} ${judge2}</span></li>`;
          };
          judge.append(`
                        ${format("颜值", "CHR")}
                        ${format("智力", "INT")}
                        ${format("体质", "STR")}
                        ${format("家境", "MNY")}
                        ${format("快乐", "SPR")}
                        ${format("享年", "AGE")}
                        ${format("总评", "SUM")}
                    `);
        }
      }
    };
    $$on("achievement", ({ name }) => {
      this.hint(`解锁成就【${name}】`, "success");
    });
  }
  switch(page) {
    const p = this.#pages[page];
    if (!p)
      return;
    $("#main").detach();
    p.clear();
    p.page.appendTo("body");
    if (typeof p.page.mounted === "function") {
      p.page.mounted();
    }
  }
  hint(message, type = "info") {
    if (this.#hintTimeout) {
      clearTimeout(this.#hintTimeout);
      this.#hintTimeout = null;
    }
    hideBanners();
    requestAnimationFrame(() => {
      const banner = $(`.banner.${type}`);
      banner.addClass("visible");
      banner.find(".banner-message").text(message);
      if (type != "error") {
        this.#hintTimeout = setTimeout(hideBanners, 3000);
      }
    });
  }
  setTheme(theme) {
    const themeLink = $(document).find("#themeLink");
    if (theme == "light") {
      themeLink.attr("href", "light.css");
    } else {
      themeLink.attr("href", "dark.css");
    }
  }
  get times() {
    return this.#life?.times || 0;
  }
  set times(v) {
    if (this.#life)
      this.#life.times = v;
  }
  get isEnd() {
    return this.#isEnd;
  }
}
var app_default = App;

// src/runtime/browser.js
function installBrowserJsonLoader(basePath = "../data", target = globalThis) {
  target.json = async (fileName) => await (await fetch(`${basePath}/${fileName}.json`)).json();
  return target.json;
}

// src/runtime/shared.js
function installEventBus(target = globalThis) {
  if (target.$$eventMap && target.$$event && target.$$on && target.$$off) {
    return {
      emit: target.$$event,
      on: target.$$on,
      off: target.$$off
    };
  }
  const eventMap = new Map;
  target.$$eventMap = eventMap;
  target.$$event = (tag, data2) => {
    const listener = eventMap.get(tag);
    if (listener)
      listener.forEach((fn) => fn(data2));
  };
  target.$$on = (tag, fn) => {
    let listener = eventMap.get(tag);
    if (!listener) {
      listener = new Set;
      eventMap.set(tag, listener);
    }
    listener.add(fn);
  };
  target.$$off = (tag, fn) => {
    const listener = eventMap.get(tag);
    if (listener)
      listener.delete(fn);
  };
  return {
    emit: target.$$event,
    on: target.$$on,
    off: target.$$off
  };
}

// src/index.js
installEventBus();
installBrowserJsonLoader();
globalThis.hideBanners = (e) => {
  document.querySelectorAll(".banner.visible").forEach((b) => b.classList.remove("visible"));
};
var app = new app_default;
app.initial();
