0 votes
by (870 points)

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?

1 Answer

0 votes
by (68.6k points)

You want to use both the <Map>.set() and <Map>.get() methods.

<<run _temp.set("Attack", _temp.get("Attack") - _effect.power)>>

NOTE: You may still run into reference issues.  Writing this on my phone, so I haven't looked over your widget much.

by (870 points)
Ah, I was aware I could do that, but I was kind of hoping to avoid that, since it's a lot to write out. I suppose I'll do that if there's no better option.
...