Skip to content

Commit

Permalink
Enabled global skill bonus modifier
Browse files Browse the repository at this point in the history
- Moved skill calcs to be after global mod calcs 
- Added 0 as fallback for most bonus values
- Added global skill modifier to skill totals, with comparison against typed per-skill bonuses in both PC and NPC skill calcs
- For NPCS, enabled power and untyped bonuses even when advanced calcs are off (on the premise that these cover the vast majority of temporary effects).
  • Loading branch information
FoxLee authored Oct 28, 2024
1 parent 8fb9339 commit e18c324
Showing 1 changed file with 108 additions and 62 deletions.
170 changes: 108 additions & 62 deletions module/actor/actor.js
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,10 @@ export class Actor4e extends Actor {
if (system.attributes.temphp.value <= 0 )
system.attributes.temphp.value = null;

// Skill modifiers
//Calc defence stats
// Calculate Defences
if(this.type === "NPC"){
this.calcSkillNPC(system);
this.calcDefenceStatsNPC(system);
} else {
this.calcSkillCharacter(system);
this.calcDefenceStatsCharacter(system);
}

Expand Down Expand Up @@ -552,28 +549,8 @@ export class Actor4e extends Actor {

if (system.movement.shift.value < 0)
system.movement.shift.value = 0;

//Passive Skills
for (let [id, pas] of Object.entries(system.passive)) {
let passiveBonusValue = 0;
if(!(pas.bonus.length === 1 && jQuery.isEmptyObject(pas.bonus[0]))) {
for( const b of pas.bonus) {
if(b.active && Helper._isNumber(b.value)) {
passiveBonusValue += parseInt(b.value);
}
else if(b.active){
let val = Helper.replaceData(b.value,system)
if(Helper._isNumber(val)){
passiveBonusValue += parseInt(val);
}
}
}
}
pas.bonusValue = passiveBonusValue;
pas.value = 10 + system.skills[pas.skill].total + passiveBonusValue;
}

//Attack and damage modifiers
//Calculate global attack, damage and skill modifiers
for (let [id, mod] of Object.entries(system.modifiers)) {
let modifierBonusValue = 0;
if(!(mod.bonus.length === 1 && jQuery.isEmptyObject(mod.bonus[0]))) {
Expand All @@ -595,6 +572,33 @@ export class Actor4e extends Actor {
mod.label = game.i18n.localize(DND4E.modifiers[id]);
}

//Calculate skill modifiers
if(this.type === "NPC"){
this.calcSkillNPC(system);
} else {
this.calcSkillCharacter(system);
}

//Passive Skills
for (let [id, pas] of Object.entries(system.passive)) {
let passiveBonusValue = 0;
if(!(pas.bonus.length === 1 && jQuery.isEmptyObject(pas.bonus[0]))) {
for( const b of pas.bonus) {
if(b.active && Helper._isNumber(b.value)) {
passiveBonusValue += parseInt(b.value);
}
else if(b.active){
let val = Helper.replaceData(b.value,system)
if(Helper._isNumber(val)){
passiveBonusValue += parseInt(val);
}
}
}
}
pas.bonusValue = passiveBonusValue;
pas.value = 10 + system.skills[pas.skill].total + passiveBonusValue;
}

/* Resistances & Weaknesses
Apr 2024 update - [type].value should be now read as the incoming damage adjustment,
with the resistance totalled under [type].res and the vulnerabilty under [type].vuln.
Expand Down Expand Up @@ -832,6 +836,23 @@ export class Actor4e extends Actor {
}

calcSkillCharacter(system){
/* Typed bonuses to global skill modifiers need to be compared against typed bonuses to the individual skill. Manual (bonus array) bonuses are assumed to total the final bonus value, minus the typed bonuses. */
let globalBonus = {};
try{
globalBonus = {
"class": system.modifiers.skills.class || 0,
"feat": system.modifiers.skills.feat || 0,
"item": system.modifiers.skills.item || 0,
"power": system.modifiers.skills.power || 0,
"race": system.modifiers.skills.race || 0,
"untyped": system.modifiers.skills.untyped || 0,
"manual": system.modifiers.skills.value - system.modifiers.skills.class - system.modifiers.skills.feat - system.modifiers.skills.item - system.modifiers.skills.power - system.modifiers.skills.race - system.modifiers.skills.untyped || 0
}
}catch(e){
console.warn(`Global skill calc failed, probably due to an unmigrated actor. Skills will function but this bonus will not be correctly applied. (Error message: "${e}")`);
globalBonus = {"class": 0,"feat": 0,"item": 0,"power": 0,"race": 0,"untyped": 0,"manual": 0};
}

for (const [id, skl] of Object.entries(system.skills)) {
skl.value = parseFloat(skl.value || 0);

Expand Down Expand Up @@ -884,35 +905,38 @@ export class Actor4e extends Actor {
switch (skl.training){
case 8:
trainingBonus = system.skillTraining.expertise.value + system.skillTraining.expertise.untyped;
featBonus = Math.max(system.skillTraining.expertise.feat, skl.feat);
itemBonus = Math.max(system.skillTraining.expertise.item, skl.item);
powerBonus = Math.max(system.skillTraining.expertise.power, skl.power);
raceBonus = Math.max(system.skillTraining.expertise.race, skl.race);
featBonus = Math.max(system.skillTraining.expertise.feat, skl.feat,0);
itemBonus = Math.max(system.skillTraining.expertise.item, skl.item,0);
powerBonus = Math.max(system.skillTraining.expertise.power, skl.power,0);
raceBonus = Math.max(system.skillTraining.expertise.race, skl.race,0);
break;
case 5:
trainingBonus = system.skillTraining.trained.value + system.skillTraining.trained.untyped;
featBonus = Math.max(system.skillTraining.trained.feat, skl.feat);
itemBonus = Math.max(system.skillTraining.trained.item, skl.item);
powerBonus = Math.max(system.skillTraining.trained.power, skl.power);
raceBonus = Math.max(system.skillTraining.trained.race, skl.race);
featBonus = Math.max(system.skillTraining.trained.feat, skl.feat,0);
itemBonus = Math.max(system.skillTraining.trained.item, skl.item,0);
powerBonus = Math.max(system.skillTraining.trained.power, skl.power,0);
raceBonus = Math.max(system.skillTraining.trained.race, skl.race,0);
break;
case 0:
trainingBonus = system.skillTraining.untrained.value + system.skillTraining.untrained.untyped;
featBonus = Math.max(system.skillTraining.untrained.feat, skl.feat);
itemBonus = Math.max(system.skillTraining.untrained.item, skl.item);
powerBonus = Math.max(system.skillTraining.untrained.power, skl.power);
raceBonus = Math.max(system.skillTraining.untrained.race, skl.race);
featBonus = Math.max(system.skillTraining.untrained.feat, skl.feat,0);
itemBonus = Math.max(system.skillTraining.untrained.item, skl.item,0);
powerBonus = Math.max(system.skillTraining.untrained.power, skl.power,0);
raceBonus = Math.max(system.skillTraining.untrained.race, skl.race,0);
}

// Compute modifier
skl.mod = system.abilities[skl.ability].mod;

skl.total = skl.value + skl.base + skl.mod + sklBonusValue + skl.effectBonus - sklArmourPenalty;
skl.total += featBonus || 0;
skl.total += itemBonus || 0;
skl.total += powerBonus || 0;
skl.total += raceBonus || 0;
skl.total += Math.max(featBonus || 0, globalBonus.feat);
skl.total += Math.max(itemBonus || 0, globalBonus.item);
skl.total += Math.max(powerBonus || 0, globalBonus.power);
skl.total += Math.max(raceBonus || 0, globalBonus.race);
skl.total += skl.untyped || 0;
skl.total += globalBonus.untyped;
//No way to sort manual bonuses, so they just get added regardless.
skl.total += globalBonus.manual;
skl.total += trainingBonus;

if(!game.settings.get("dnd4e", "halfLevelOptions")) {
Expand All @@ -924,6 +948,23 @@ export class Actor4e extends Actor {
}

calcSkillNPC(system){
/* Typed bonuses to global skill modifiers need to be compared against typed bonuses to the individual skill. Manual (bonus array) bonuses are assumed to total the final bonus value, minus the typed bonuses. */
let globalBonus = {};
try{
globalBonus = {
"class": system.modifiers.skills.class || 0,
"feat": system.modifiers.skills.feat || 0,
"item": system.modifiers.skills.item || 0,
"power": system.modifiers.skills.power || 0,
"race": system.modifiers.skills.race || 0,
"untyped": system.modifiers.skills.untyped || 0,
"manual": system.modifiers.skills.value - system.modifiers.skills.class - system.modifiers.skills.feat - system.modifiers.skills.item - system.modifiers.skills.power - system.modifiers.skills.race - system.modifiers.skills.untyped || 0
}
}catch(e){
console.warn(`Global skill calc failed, probably due to an unmigrated actor. Skills will function but this bonus will not be correctly applied. (Error message: "${e}")`);
globalBonus = {"class": 0,"feat": 0,"item": 0,"power": 0,"race": 0,"untyped": 0,"manual": 0};
}

for (let [id, skl] of Object.entries(system.skills)) {
skl.value = parseFloat(skl.value || 0);

Expand Down Expand Up @@ -967,43 +1008,42 @@ export class Actor4e extends Actor {
}

// Compute modifier
skl.mod = system.abilities[skl.ability].mod;
let powerBonus = 0;

if(system.advancedCals){

let trainingBonus = 0;
let featBonus = 0;
let itemBonus = 0;
let powerBonus = 0;
let raceBonus = 0;
let trainingBonus = 0;
skl.mod = system.abilities[skl.ability].mod;

switch (skl.training){
case 8:
trainingBonus = system.skillTraining.expertise.value + system.skillTraining.expertise.untyped;
featBonus = Math.max(system.skillTraining.expertise.feat, skl.feat);
itemBonus = Math.max(system.skillTraining.expertise.item, skl.item);
powerBonus = Math.max(system.skillTraining.expertise.power, skl.power);
raceBonus = Math.max(system.skillTraining.expertise.race, skl.race);
featBonus = Math.max(system.skillTraining.expertise.feat, skl.feat,0);
itemBonus = Math.max(system.skillTraining.expertise.item, skl.item,0);
powerBonus = Math.max(system.skillTraining.expertise.power, skl.power,0);
raceBonus = Math.max(system.skillTraining.expertise.race, skl.race,0);
break;
case 5:
trainingBonus = system.skillTraining.trained.value + system.skillTraining.trained.untyped;
featBonus = Math.max(system.skillTraining.trained.feat, skl.feat);
itemBonus = Math.max(system.skillTraining.trained.item, skl.item);
powerBonus = Math.max(system.skillTraining.trained.power, skl.power);
raceBonus = Math.max(system.skillTraining.trained.race, skl.race);
featBonus = Math.max(system.skillTraining.trained.feat, skl.feat,0);
itemBonus = Math.max(system.skillTraining.trained.item, skl.item,0);
powerBonus = Math.max(system.skillTraining.trained.power, skl.power,0);
raceBonus = Math.max(system.skillTraining.trained.race, skl.race,0);
break;
case 0:
trainingBonus = system.skillTraining.untrained.value + system.skillTraining.untrained.untyped;
featBonus = Math.max(system.skillTraining.untrained.feat, skl.feat);
itemBonus = Math.max(system.skillTraining.untrained.item, skl.item);
powerBonus = Math.max(system.skillTraining.untrained.power, skl.power);
raceBonus = Math.max(system.skillTraining.untrained.race, skl.race);
featBonus = Math.max(system.skillTraining.untrained.feat, skl.feat,0);
itemBonus = Math.max(system.skillTraining.untrained.item, skl.item,0);
powerBonus = Math.max(system.skillTraining.untrained.power, skl.power,0);
raceBonus = Math.max(system.skillTraining.untrained.race, skl.race,0);
}

skl.total = skl.value + skl.base + skl.mod + sklBonusValue + skl.effectBonus - sklArmourPenalty;
skl.total += featBonus || 0;
skl.total += itemBonus || 0;
skl.total += powerBonus || 0;
skl.total += raceBonus || 0;
skl.total += skl.untyped || 0;
skl.total += Math.max(featBonus || 0, globalBonus.feat);
skl.total += Math.max(itemBonus || 0, globalBonus.item);
skl.total += Math.max(raceBonus || 0, globalBonus.race);
skl.total += trainingBonus;

if(!game.settings.get("dnd4e", "halfLevelOptions")) {
Expand All @@ -1013,6 +1053,12 @@ export class Actor4e extends Actor {
} else {
skl.total = skl.base;
}

skl.total += Math.max(powerBonus || 0, globalBonus.power);
skl.total += skl.untyped || 0;
skl.total += globalBonus.untyped;
//No way to sort manual bonuses, so they just get added regardless.
skl.total += globalBonus.manual;

skl.label = skl.label? skl.label : game.i18n.localize(DND4E.skills[id]);
}
Expand Down

0 comments on commit e18c324

Please sign in to comment.