I've recently decided to rehaul how stats are formatted in my RPG engine. Instead of making every stat its own attribute in a character object, I classed all stats under a Map object with keys for every stat, with each reference pointing to an object with separate attributes for base, effective, and modified stats.
class Actor {
...
this.stats = new Map([
["Attack",{Base: atk, Eff: atk, Mods: 0}],
["Defense",{Base: def, Eff: def, Mods: 0}],
["Special",{Base: spc, Eff: spc, Mods: 0}]
]);
...
calcStats (){
this.stats.forEach( (v,k) => {
v.Eff = v.Base + v.Mods;
if (v.Eff < 0){
v.Eff = 0;
}
} );
}
This has mostly worked very well, but stat buffs and debuffs are a little tricky. Currently, I'm running this widget every time a status effect is applied:
<<widget "effectmanager">>
<<set _member = $args[0]>>
<<set _tempAtk = 0>>
<<set _tempDef = 0>>
<<set _tempSpc = 0>>
<<for _j, _effect range _member.effects>>
...
<<if _effect.statmod is true>>
<<switch _effect.name>>
<<case "Injury">>
<<set _tempAtk -= _effect.power>>
<<case "Pain">>
<<set _tempDef -= _effect.power>>
<<case "Headache">>
<<set _tempSpc -= _effect.power>>
<<case "Knocked Down">>
<<set _tempDef -= Math.round(_member.getBase("Defense")/2)>>
<<set _member.down to true>>
<<case "Curse">>
<<set _tempAtk -= _effect.power>>
<<set _tempDef -= _effect.power>>
<<set _tempSpc -= _effect.power>>
<<case "Forsaken">>
<<set _tempDef -= _effect.power>>
<<set _member.forsaken to true>>
<<case "ATK Boost">>
<<set _tempAtk += _effect.power>>
<<case "DEF Boost">>
<<set _tempDef += _effect.power>>
<<case "SPC Boost">>
<<set _tempSpc += _effect.power>>
<<case "Blessing">>
<<set _tempAtk += _effect.power>>
<<set _tempDef += _effect.power>>
<<set _tempSpc += _effect.power>>
<<case "Protector">>
<<set _member.protector to true>>
<<set _tempDef += _effect.power>>
<</switch>>
<</if>>
/* Reaper: remove expired effects and undo their effects. */
<<if _effect.duration eq 0>>
...
<<if _effect.statmod is true>>
<<switch _effect.name>>
<<case "Injury">>
<<set _tempAtk += _effect.power>>
<<case "Pain">>
<<set _tempDef += _effect.power>>
<<case "Headache">>
<<set _tempSpc += _effect.power>>
<<case "Knocked Down">>
<<set _tempDef += Math.round(_member.getBase("Defense")/2)>>
<<set _member.down to false>>
<<case "Curse">>
<<set _tempAtk += _effect.power>>
<<set _tempDef += _effect.power>>
<<set _tempSpc += _effect.power>>
<<case "Forsaken">>
<<set _tempDef += _effect.power>>
<<set _member.forsaken to false>>
<<case "ATK Boost">>
<<set _tempAtk -= _effect.power>>
<<case "DEF Boost">>
<<set _tempDef -= _effect.power>>
<<case "SPC Boost">>
<<set _tempSpc -= _effect.power>>
<<case "Blessing">>
<<set _tempAtk -= _effect.power>>
<<set _tempDef -= _effect.power>>
<<set _tempSpc -= _effect.power>>
<</switch>>
<</if>>
<<removemessage _member _effect.name>>
<<run _member.effects.deleteAt(_j)>>
<</if>>
<</for>>
/* stat mods */
<<run _member.setMod("Attack",_tempAtk)>>
<<run _member.setMod("Defense",_tempDef)>>
<<run _member.setMod("Special",_tempSpc)>>
<<run _member.calcStats()>>
<</widget>>
Since I want to make this engine adaptable for other types of RPGs, I'd prefer to make the "temp stats" a Map corresponding to the stats Map, which would allow for loops to automate the process of creation at the beginning and setting at the end. However, I run into the problem that this
<<set _temp.get("Attack") -= _effect.power>>
is not a valid <<set>> expression. I'd have to end up creating individual variables and assigning them the Map's values anyway, which defeats the purpose.
Is there an easy way to accomplish this, or a better algorithm for doing this in the first place?