text: !(function(e, t) {
  "object" == typeof exports && "undefined" != typeof module
  ? t(exports)
  : "function" == typeof define && define.amd
  ? define(["exports"], t)
  : t((e.PF2EncounterPlanner = {}));
  })(this, function(e) {
  "use strict";
  const t = {
  FIGHT: "fight_component",
  ACCOMPLISHMENT: "accomplishment_component",
  HAZARD: "hazard_component",
  CUSTOM: "custom_component"
  };
  class n {
  constructor(e = 4, n = 1) {
  (this.type = t.FIGHT),
  (this.creatures = []),
  (this.expectedPlayers = e),
  (this.expectedLevel = n);
  }
  getEncounterXpPerPlayer() {
  let e = 0;
  for (let t of this.creatures)
  if (null != t.level && null != t.amount) {
  let n = t.level - this.expectedLevel;
  if (n > 4) return null;
  n >= -4 && (e += t.amount * r[n.toString()]);
  }
  return (e *= 4 / this.expectedPlayers), e;
  }
  getEncounterRating() {
  let e = this.getEncounterXpPerPlayer();
  if (null == e) return l.IMPOSSIBLE;
  let t = 200,
  n = null;
  for (let s in i) t > i[s] && i[s] + 10 >= e && ((n = s), (t = i[s]));
  return null == n ? l.IMPOSSIBLE : n;
  }
  exportToJSON() {
  let e = {
  type: t.FIGHT,
  expectedLevel: this.expectedLevel,
  expectedPlayers: this.expectedPlayers,
  creatures: []
  };
  for (let t of this.creatures) e.creatures.push(t.exportToJSON());
  return e;
  }
  static importFromJSON(e) {
  let t = new n(e.expectedPlayers || 4, e.expectedLevel || 1);
  for (let n of e.creatures) t.creatures.push(s.importFromJSON(n));
  return t;
  }
  getNewCreatureId() {
  let e,
  t = !1;
  do {
  e = "creature-" + Math.floor(Number.MAX_SAFE_INTEGER * Math.random());
  for (let n of this.creatures)
  if (n.id === e) {
  t = !0;
  break;
  }
  } while (t);
  return e;
  }
  copy() {
  return n.importFromJSON(this.exportToJSON());
  }
  getTooltip() {
  let e = [];
  for (let t of this.creatures) {
  let n = null != t.name && t.name.length > 0 ? t.name : "Unnamed creature";
  (n = `<strong>[Lvl ${null != t.level ? t.level : "?"}]</strong> ` + n),
  (n += " &times; " + (null != t.amount ? t.amount : "?")),
  e.push(n);
  }
  return e.join("<br />");
  }
  }
  class s {
  constructor(e, t, n, s = null, l = null) {
  (this.name = e),
  (this.level = t),
  (this.amount = n),
  (this.link = s),
  (this.id = l);
  }
  exportToJSON() {
  return {
  name: this.name,
  level: this.level,
  amount: this.amount,
  link: this.link,
  id: this.id
  };
  }
  static importFromJSON(e) {
  return new s(e.name, e.level, e.amount, e.link, e.id);
  }
  }
  const l = {
  TRIVIAL: "trivial",
  LOW: "low",
  MODERATE: "moderate",
  SEVERE: "severe",
  EXTREME: "extreme",
  IMPOSSIBLE: "impossible"
  },
  r = {
  "-4": 10,
  "-3": 15,
  "-2": 20,
  "-1": 30,
  0: 40,
  1: 60,
  2: 80,
  3: 120,
  4: 160
  },
  i = {trivial: 40, low: 60, moderate: 80, severe: 120, extreme: 160};
  class o {
  constructor(e = a.MINOR) {
  (this.type = t.ACCOMPLISHMENT), (this.level = e);
  }
  getTooltip() {
  return this.level;
  }
  getEncounterXpPerPlayer() {
  switch (this.level) {
  case a.MAJOR:
  return 80;
  case a.MODERATE:
  return 30;
  default:
  return 10;
  }
  }
  exportToJSON() {
  return {type: this.type, level: this.level};
  }
  static importFromJSON(e) {
  return new o(e.level);
  }
  }
  const a = {
  MINOR: "Minor accomplishment",
  MODERATE: "Moderate accomplishment",
  MAJOR: "Major accomplishment"
  };
  class p {
  constructor(e = 1) {
  (this.type = t.HAZARD), (this.expectedLevel = e), (this.hazards = []);
  }
  getEncounterXpPerPlayer() {
  let e = 0;
  for (let t of this.hazards)
  if (null != t.level && null != t.amount) {
  let n = t.level - this.expectedLevel;
  if (n > 4) return null;
  n >= -4 && (e += t.amount * u[n.toString()] * (t.isComplex ? 5 : 1));
  }
  return e;
  }
  exportToJSON() {
  let e = {type: t.HAZARD, hazards: [], expectedLevel: this.expectedLevel};
  for (let t of this.hazards) e.hazards.push(t.exportToJSON());
  return e;
  }
  static importFromJSON(e) {
  let t = new p(e.expectedLevel || 1);
  for (let n of e.hazards) t.hazards.push(m.importFromJSON(n));
  return t;
  }
  getNewHazardId() {
  let e,
  t = !1;
  do {
  e = "hazard-" + Math.floor(Number.MAX_SAFE_INTEGER * Math.random());
  for (let n of this.hazards)
  if (n.id === e) {
  t = !0;
  break;
  }
  } while (t);
  return e;
  }
  copy() {
  return p.importFromJSON(this.exportToJSON());
  }
  getTooltip() {
  let e = [];
  for (let t of this.hazards) {
  let n = null != t.name && t.name.length > 0 ? t.name : "Unnamed hazard";
  (n = `<strong>[Lvl ${null != t.level ? t.level : "?"}]</strong> ` + n),
  (n += " &times; " + (null != t.amount ? t.amount : "?")),
  e.push(n);
  }
  return e.join("<br />");
  }
  }
  class m {
  constructor(e, t, n, s, l = null, r = null) {
  (this.name = e),
  (this.level = t),
  (this.isComplex = n),
  (this.amount = s),
  (this.link = l),
  (this.id = r);
  }
  exportToJSON() {
  return {
  name: this.name,
  level: this.level,
  isComplex: this.isComplex,
  amount: this.amount,
  link: this.link,
  id: this.id
  };
  }
  static importFromJSON(e) {
  return new m(e.name, e.level, e.isComplex, e.amount, e.link, e.id);
  }
  }
  const u = {
  "-4": 2,
  "-3": 3,
  "-2": 4,
  "-1": 6,
  0: 8,
  1: 12,
  2: 16,
  3: 24,
  4: 32
  };
  class h {
  constructor(e) {
  (this.type = t.CUSTOM), (this.xp = e);
  }
  getEncounterXpPerPlayer() {
  return parseInt(this.xp);
  }
  getTooltip() {
  return "";
  }
  exportToJSON() {
  return {type: this.type, xp: this.xp};
  }
  static importFromJSON(e) {
  return new h(e.xp);
  }
  }
  class c {
  constructor(e) {
  (this.name = e), (this.id = null), (this.component = null);
  }
  registerToSession(e) {
  do {
  this.id = "element-" + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
  } while (e.isElementIdUsed(this.id));
  }
  exportToJSON() {
  return {
  name: this.name,
  id: this.id,
  component: null == this.component ? null : this.component.exportToJSON()
  };
  }
  static importFromJSON(e) {
  let s = new c(e.name);
  if (((s.id = e.id), null != e.component))
  switch (e.component.type) {
  case t.FIGHT:
  s.component = n.importFromJSON(e.component);
  break;
  case t.ACCOMPLISHMENT:
  s.component = o.importFromJSON(e.component);
  break;
  case t.HAZARD:
  s.component = p.importFromJSON(e.component);
  break;
  case t.CUSTOM:
  s.component = h.importFromJSON(e.component);
  }
  return s;
  }
  }
  class d {
  constructor(e = "Default", t = null) {
  (this.name = e),
  (this.id = null != t ? t : d.getIdFriendlyName(e)),
  (this.elements = []);
  }
  static getIdFriendlyName(e) {
  return (
  "element-" +
  e
  .toLowerCase()
  .split(/[^a-z0-9]/)
  .filter(e => e.length > 0)
  .join("-")
  );
  }
  moveElement(e, t) {
  let n = this.elements[e];
  this.elements.splice(e, 1), this.elements.splice(t, 0, n);
  }
  exportToJSON() {
  let e = {name: this.name, id: this.id, elements: []};
  for (let t of this.elements) e.elements.push(t.exportToJSON());
  return e;
  }
  static importFromJSON(e) {
  let t = new d(e.name, e.id);
  for (let n of e.elements) t.elements.push(c.importFromJSON(n));
  return t;
  }
  }
  class v {
  constructor(e, t, n = null, s = !1, l = 0) {
  (this.id = e),
  (this.players = t),
  (this.element = n),
  (this.levelUp = s || !1),
  (this.additionalNPCs = isNaN(l) ? 0 : parseInt(l));
  }
  exportToJSON() {
  return {
  id: this.id,
  players: this.players,
  element: null == this.element ? null : this.element.id,
  levelUp: this.levelUp,
  additionalNPCs: this.additionalNPCs
  };
  }
  static importFromJSON(e, t) {
  return new v(
  e.id,
  e.players,
  t.findElementById(e.element),
  e.levelUp,
  e.additionalNPCs
  );
  }
  }
  class f {
  constructor(e, t = []) {
  (this.events = null == t ? [] : t),
  (this.session = e),
  (this.playerHistory = {}),
  (this.errorEvents = []),
  (this.xpChange = {}),
  this.computeTimeline(),
  (this.listener = null);
  }
  addEvent(e, t, n = !1) {
  let s, l, r;
  if (!n && ((s = session.findElementById(t)), null == s)) return null;
  do {
  (l = "event-" + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)),
  (r = null != this.events.find(e => e.id === l));
  } while (r);
  let i = new v(
  l,
  session.params.players.map(e => e.id),
  s,
  n
  );
  return (
  this.events.splice(e, 0, i),
  this.session.saveSession(),
  this.computeTimeline(),
  i
  );
  }
  deleteEvent(e) {
  let t = this.events.findIndex(t => t.id === e);
  null != t &&
  (this.events.splice(t, 1), this.session.saveSession(), this.computeTimeline());
  }
  moveEvent(e, t) {
  this.events.splice(t, 0, ...this.events.splice(e, 1)),
  this.session.saveSession(),
  this.computeTimeline();
  }
  computeTimeline() {
  (this.playerHistory = {}), (this.errorEvents = []), (this.xpChange = {});
  for (let e of this.session.params.players)
  this.playerHistory[e.id] = [
  {index: -1, level: parseInt(e.level), xp: parseInt(e.xp)}
  ];
  let e = 0;
  for (let n of this.events) {
  if (n.levelUp) {
  let t = !1;
  for (let s of n.players) {
  let n = this.playerHistory[s];
  n &&
  n.last().xp >= 1e3 &&
  (n.push({index: e, level: n.last().level + 1, xp: n.last().xp - 1e3}),
  (t = !0));
  }
  t || this.errorEvents.push({event: n, reason: "No player could level up"});
  } else if (null != n.element && null != n.element.component) {
  let s = n.element.component,
  r = [];
  for (let e of n.players) {
  let t = this.playerHistory[e];
  t && r.push(Object.assign({id: e}, t.last()));
  }
  if (r.length) {
  let i,
  o = Math.max(...Object.values(this.playerHistory).map(e => e.last().level)),
  a = Math.min(
  ...Object.values(this.playerHistory)
  .filter(e => e.last().level === o)
  .map(e => 1e3 * e.last().level + e.last().xp)
  );
  switch (s.type) {
  case t.FIGHT:
  let e = this.session.getPlayerGroupLevel(r),
  o = s.expectedPlayers,
  a = s.expectedLevel;
  (s.expectedLevel = e), (s.expectedPlayers = r.length + n.additionalNPCs);
  let p = s.getEncounterRating();
  this.session.params.allowImpossibleEncounters || p !== l.IMPOSSIBLE
  ? (i = s.getEncounterXpPerPlayer())
  : (this.errorEvents.push({event: n, reason: "Impossible encounter"}),
    (i = null)),
  (s.expectedLevel = a),
  (s.expectedPlayers = o);
  break;
  case t.HAZARD:
  let m = this.session.getPlayerGroupLevel(r),
  u = s.expectedLevel;
  (s.expectedLevel = m), (i = s.getEncounterXpPerPlayer()), (s.expectedLevel = u);
  break;
  default:
  i = s.getEncounterXpPerPlayer();
  }
  (this.xpChange[n.id] = i), (i = i || 0);
  for (let t of r) {
  t.level < o && console.log(t, o, a);
  let n =
  t.level < o
  ? i * Math.max(1, this.session.params.underLeveledPlayerMultiplier)
  : i;
  (n = Math.max(i, Math.min(n, i + a - 1e3 * t.level - t.xp))),
  this.playerHistory[t.id].push({index: e, xp: t.xp + n, level: t.level});
  }
  } else this.errorEvents.push({event: n, reason: "No player for this event"});
  } else this.errorEvents.push({event: n, reason: "Invalid event"});
  e++;
  }
  null != this.listener && this.listener();
  }
  }
  Array.prototype.last = function() {
  return this[this.length - 1];
  };
  class E {
  constructor(e = "Default", t = {}, n = null) {
  (this.name = e),
  (this.params = Object.assign(
  {
  autoLevelUp: !1,
  allowImpossibleEncounters: !1,
  groupLevelFunction: y.MAX_PLAYER_LEVEL,
  underLeveledPlayerMultiplier: 2,
  players: [
  {name: "Player 1", level: 1, xp: 0, id: "player-1"},
  {name: "Player 2", level: 1, xp: 0, id: "player-2"},
  {name: "Player 3", level: 1, xp: 0, id: "player-3"},
  {name: "Player 4", level: 1, xp: 0, id: "player-4"}
  ]
  },
  t
  )),
  (this.encounters = []),
  (this.timeline = n);
  }
  addEncounter(e) {
  let t = d.getIdFriendlyName(e);
  if (null != this.encounters.find(t => t.name === e)) return null;
  if (null != this.encounters.find(e => e.id === t)) {
  let e = 0;
  for (; null != this.encounters.find(n => n.id === `${t}${e}`); ) e++;
  t = `${t}${e}`;
  }
  let n = new d(e, t);
  return this.encounters.push(n), this.saveSession(), n;
  }
  registerElement(e, t) {
  let n = this.encounters.find(t => t.name === e);
  return (
  null != n && (t.registerToSession(this), n.elements.push(t)),
  this.saveSession(),
  null != n
  );
  }
  isElementIdUsed(e) {
  for (let t of this.encounters)
  for (let n of t.elements) if (n.id === e) return !0;
  return !1;
  }
  moveEncounterIndex(e, t) {
  let n = this.encounters[e];
  this.encounters.splice(e, 1), this.encounters.splice(t, 0, n);
  }
  renameEncounter(e, t) {
  let n = this.encounters.find(t => t.name === e);
  return (
  null == this.encounters.find(e => e.name === t) &&
  null != n &&
  ((n.name = t), this.saveSession(), !0)
  );
  }
  removeEncounter(e) {
  this.encounters.splice(
  this.encounters.findIndex(t => t.id === e.id),
  1
  ),
  this.saveSession();
  }
  saveSession() {
  let e = JSON.parse(window.localStorage.getItem("sessions:list")) || [];
  e.findIndex(e => e === this.name) < 0 && e.push(this.name),
  window.localStorage.setItem("sessions:list", JSON.stringify(e)),
  window.localStorage.setItem("session:" + this.name, this.exportToJSON());
  }
  changeName(e) {
  let t = JSON.parse(window.localStorage.getItem("sessions:list")) || [],
  n = t.findIndex(e => e === this.name);
  -1 !== n && t.splice(n, 1),
  window.localStorage.setItem("sessions:list", JSON.stringify(t)),
  (this.name = e),
  this.saveSession();
  }
  exportToJSON() {
  let e = {
  params: this.params,
  name: this.name,
  encounters: [],
  timelineEvents: []
  };
  for (let t of this.encounters) e.encounters.push(t.exportToJSON());
  for (let t of this.timeline.events) e.timelineEvents.push(t.exportToJSON());
  return JSON.stringify(e);
  }
  addPlayer(e, t, n) {
  let s, l;
  do {
  (s = "player-" + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)),
  (l = null != this.params.players.find(e => e.id === s));
  } while (l);
  return this
  .params.players[this.params.players.push({id: s, name: e, level: t, xp: n}) - 1];
  }
  getPlayerGroupLevel(e = null) {
  switch (
  ((e = null == e ? this.params.players : e), this.params.groupLevelFunction)
  ) {
  case y.MIN_PLAYER_LEVEL:
  return Math.min(...e.map(e => e.level));
  case y.AVERAGE_PLAYER_LEVEL_FLOOR:
  return Math.floor(e.map(e => e.level).reduce((e, t) => e + t, 0) / e.length);
  case y.AVERAGE_PLAYER_LEVEL_CEIL:
  return Math.ceil(e.map(e => e.level).reduce((e, t) => e + t, 0) / e.length);
  default:
  return Math.max(...e.map(e => e.level));
  }
  }
  findElementById(e) {
  return this.encounters.flatMap(e => e.elements).find(t => t.id === e);
  }
  static importFromJSON(e) {
  let t = JSON.parse(e),
  n = new E(t.name, t.params);
  for (let e of t.encounters) n.encounters.push(d.importFromJSON(e));
  let s = [];
  if (null != t.timelineEvents)
  for (let e of t.timelineEvents) s.push(v.importFromJSON(e, n));
  return (n.timeline = new f(n, s)), n;
  }
  static makeSession(e = "Default") {
  let t = window.localStorage.getItem("session:" + e),
  n = null == t ? new E(e) : E.importFromJSON(t);
  return null == t && (n.timeline = new f(n)), n;
  }
  }
  const y = {
  MIN_PLAYER_LEVEL: "Minimum player level",
  MAX_PLAYER_LEVEL: "Maximum player level",
  AVERAGE_PLAYER_LEVEL_FLOOR: "Average player level (floor)",
  AVERAGE_PLAYER_LEVEL_CEIL: "Average player level (ceil)"
  };
  (e.Session = E),
  (e.GroupLevelFunction = y),
  (e.Encounter = d),
  (e.EncounterElement = c),
  (e.CustomComponent = h),
  (e.FightComponent = n),
  (e.Creature = s),
  (e.EncounterRating = l),
  (e.AccomplishmentComponent = o),
  (e.AccomplishmentLevel = a),
  (e.HazardComponent = p),
  (e.Hazard = m),
  (e.ComponentType = t),
  (e.ComponentTypeName = {
  fight_component: "Fight",
  accomplishment_component: "Accomplishment",
  hazard_component: "Hazard",
  custom_component: "Custom"
  }),
  Object.defineProperty(e, "__esModule", {value: !0});
  });
  