0 votes
by (870 points)

[Disclaimer: I know I'm using ES6 components and it's problematic, for now it's easier for me]

In my RPG, I want to implement a feature like Final Fantasy's "Cover" ability or Knight's guarding ability from Bonfire: The character picks an ally to guard, and all enemy attacks that would target that ally get redirected to the guarding character instead.

However, I'm struggling with how to implement this, not just syntatically but algorithmically. I have a working prototype, but it's buggy and a horrible mess, coding-wise.

The ability functions as a status effect, defined in JavaScript:

window.Protector = class Protector extends Effect {
		this.target = target;

Here is the code for how the effect is created and applied to the characters:

<<if _Effect_to_be_applied.name is "Protector">>
		<<print _target.addEffect(new Protector(_E.name,$args[2],$power,$target[1]))>>
		<<set $target[0].protected to true>>
		<<set _p = "\'"+_target.name+"\'">>
		<<set $target[0].protectedBy = _p>>

...where _target is the guarding character, and $target is the guarded ally. $target[0] is the character object, while $target[1] is the character's variable name as a string.

This interacts with enemy targeting AI as follows:

<<if $target[0] isnot null and $target[0].protected>>
	<<find "$puppets" "name" $target[0].protectedBy>>
	<<if _pos eq -1>>
		<<set _targetingMsg = "ERROR: Protected character with no protector\n">>
		<<print "ERROR: Protected character with no protector\n">>
		<<set _targetingMsg = $puppets[_pos].name+" took the hit for "+$target[0].name+"!\n">>
		<<print $puppets[_pos].name+" took the hit for "+$target[0].name+"!\n">>
		<<set $target to [$puppets[_pos],"$puppets["+_pos+"]"]>>

...where <<find>> is a custom widget with the following definition:

<<widget "find">>
/* args0 is target array, args1 is attribute, args2 is key value */
/* Note that if your key is a string, it must be in quotes in the final code, which means you must send it to the widget with an extra pair of literal quotes (preceeded by the escape slash) */
<<print '<<set _pos = '+$args[0]+'.map(function(x) { return(x.'+$args[1]+') }).indexOf('+$args[2]+')>>'>>

This part isn't too bad, but things get complicated when I have to make a handler for the effect expiring. I handle this with an instance function on the effect object that's called upon its removal:

this.onRemove = function (puppet) {
					puppet.protector = false;
					var party;
					if (puppet instanceof Puppet){
						party = V().puppets;
					} else if (puppet instanceof Enemy) {
						party = V().enemies;
					var pos = party.map(function(x) { return(x.protectedBy) }).indexOf("\'"+puppet.name+"\'");
					if (pos > -1){
						party[pos].protected = false;
						party[pos].protectedBy = null;

I also have to include a handler for removing the effect if the protected character dies, which I added as part of the widget for applying the "dead" tag to defeated characters:

<<widget "deathcheck">>
<<if $args.length > 0>>
	<<set _targ = $args[0]>>
	<<set _targ = $target[0]>>
<<if _targ.hp <= 0 and _targ.dead is false>>
	<<set _targ.hp = 0>>
	<<for _k _effect range _targ.effects>>
		<<run _targ.removeEffect(_effect,'pierce')>>
	<<if _targ instanceof Enemy>>
		<<run $B.kills.push(_targ.name)>>
	<<if _targ.deathMessage is "special">>
		<<goto "specialdeath">>
		<<set _targ.dead to true>>
		<<print _targ.deathMessage>>
	<<if _targ.protected is true>>
		<<if _targ instanceof Puppet>>
		  <<find "$puppets" "name" _targ.protectedBy>>
		  <<if _pos > -1>>
			  <<set _p = _pos>>
			  <<set _a = "$puppets["+_pos+"].effects">>
			  <<find _a "name" "\'Protector\'">>
			  <<print $puppets[_p].removeEffect(_a[_pos],'pierce')>>
		<<elseif _targ instanceof Enemy>>
		  <<find "$enemies" "name" _targ.protectedBy>>
		  <<if _pos > -1>>
			  <<set _p = _pos>>
			  <<set _a = "$enemies["+_pos+"].effects">>
			  <<find _a "name" "\'Protector\'">>
			  <<print $enemies[_p].removeEffect(_a[_pos],'pierce')>>

This, uh, works, at least for now, but as you can see it's really sloppy and fragile code. It's likely to create tons of bugs if I change the slightest thing. Especially since I want my engine to be easily modified and usable by other authors, I'm wondering if I should do this differently.

If anyone here is a CS expert, do any ways of streamlining the code jump out at you? Is this a solved problem in RPG design, and there's a simpler algorithm I've overlooked?

Please log in or register to answer this question.