Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ baseMobXp = mobLevel^2 * ZEM
- *A Froglok* — a level 3 mob in **Innothule Swamp**:

```
baseMobXp = 3^2 * ZEM
baseMobXp = 3^2 * ZEM = 9 * ZEM
```

- *Lord Bob* — a level 65 mob in **Velk's Labyrinth** (Velketor's Labyrinth):

```
baseMobXp = 65^2 * ZEM
baseMobXp = 65^2 * ZEM = 4,225 * ZEM
```

These are the base values before the group bonus, consider modifier, and 11%
Expand Down
3 changes: 3 additions & 0 deletions src/award.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const PER_MOB_CAP = 0.11;
* @property {number} share proportional share of the party total (0-1)
* @property {number} xp XP this character receives for the kill (capped)
* @property {boolean} capApplied true if the 11% per-mob cap clamped this slice
* @property {boolean} eligible false when the character is too far below the
* group's highest level to receive any XP
*/

/**
Expand Down Expand Up @@ -54,6 +56,7 @@ export function awardXp(party, mobLevel, zem) {
share: a.share,
xp: capApplied ? cap : uncapped,
capApplied,
eligible: a.eligible,
});
}),
),
Expand Down
3 changes: 3 additions & 0 deletions src/kills.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { awardXp } from "./award.js";
* @property {number} remaining XP still needed to reach the next level
* @property {number} kills kills needed (Infinity if xpPerKill is 0)
* @property {boolean} capApplied true if the 11% per-mob cap clamped xpPerKill
* @property {boolean} eligible false when the character is too far below the
* group's highest level to receive any XP
*/

/**
Expand Down Expand Up @@ -50,6 +52,7 @@ export function killsToNextLevel(party, mobLevel, zem) {
remaining,
kills,
capApplied: a.capApplied,
eligible: a.eligible,
});
});

Expand Down
13 changes: 10 additions & 3 deletions src/split.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { groupXpEligibility } from "./eligibility.js";
* @typedef {Object} Allocation
* @property {object} character the character this share belongs to
* @property {number} share fraction of the XP pool (0-1); shares sum to 1
* @property {boolean} eligible false when the character is too far below the
* group's highest level to receive any XP (groupXpEligibility check)
*/

/**
Expand All @@ -46,15 +48,20 @@ export function splitXp(party) {
}
const { characters, penaltiesInEffect, maxLevel } = party;

const weights = characters.map((c) => {
if (!groupXpEligibility(c.level, maxLevel)) return 0;
const eligible = characters.map((c) => groupXpEligibility(c.level, maxLevel));
const weights = characters.map((c, i) => {
if (!eligible[i]) return 0;
return penaltiesInEffect ? c.xpToNextLevel : totalXpToLevel(c.level, 1);
});
const total = weights.reduce((sum, w) => sum + w, 0);

return Object.freeze(
characters.map((c, i) =>
Object.freeze({ character: c, share: weights[i] / total }),
Object.freeze({
character: c,
share: weights[i] / total,
eligible: eligible[i],
}),
),
);
}
23 changes: 17 additions & 6 deletions src/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ function refresh() {
if (player.capApplied) {
r.gk.appendChild(el("span", { class: "cap-flag" }, " *"));
r.gk.title = "11% per-mob cap applied — excess XP is lost";
} else if (!player.eligible) {
r.gk.appendChild(el("span", { class: "cap-flag" }, " †"));
r.gk.title =
"Character is too far below the highest party member to receive XP";
} else if (player.xpPerKill === 0) {
r.gk.appendChild(el("span", { class: "cap-flag" }, " ‡"));
r.gk.title = "Mob cons green to the highest party member — no XP awarded";
} else {
r.gk.title = "";
}
Expand All @@ -177,12 +184,16 @@ function refresh() {
r.xr.textContent = fmtNum(player.remaining) + " XP";
r.xc.textContent = fmtNum(player.character.xpToNextLevel) + " XP";

r.kl.textContent = Number.isFinite(player.kills)
? fmtNum(player.kills)
: dash;
r.tm.textContent = Number.isFinite(player.kills)
? fmtMins(player.kills * state.enc.minutesPerKill)
: dash;
if (Number.isFinite(player.kills)) {
r.kl.textContent = fmtNum(player.kills);
r.tm.textContent = fmtMins(player.kills * state.enc.minutesPerKill);
} else if (!player.eligible) {
r.kl.textContent = "—†";
r.tm.textContent = "—†";
} else {
r.kl.textContent = "—‡";
r.tm.textContent = "—‡";
}
});

// Totals row.
Expand Down
Loading