0
\$\begingroup\$

I'm implementing "On Attack" and "On Receive Damage" within my game and from these two answers(Implementing flexible buff system,Implementing attack class in RPG ) I got that I will be implementing custom handlers which will modify the attack effects. I've included an example below.

My question is - how to handle status changing special effects, such as knockdown, poison over time, petrify, etc? Do I need include them in my attack/damage class and have defender's damage handlers adjust the attack parameters, or is there a better way to handle special effects?

@interface Attack : NSObject

-(id)initWithDamage:(int)damage type:(int)damageType;

@property (nonatomic)int type;
@property (nonatomic)int value;
@property (nonatomic)int penetration;
@property (nonatomic,strong)NSMutableArray* attackEffects;
@end

Here's the understanding that's starting to form in my head:

  • Attacker records everything about the attack in an Attack class:
  • Create Attack class for each attack
  • Record damage type(physical/magical) and value
  • Pass Attack to the attacker's (onAttack) handlers
  • Each effect within attackers list of buffs applies it's effects to attack,(ex: poison over time, knockdown)
  • Pass the attack over to defender for his (onReceiveDamage) buffs to handle
  • Defender's list of buffs marked "on receive damage" modifies the attack (poison immunity removes poison over time)
  • Pass attack over to the defender to resolve
  • Check for special conditions (ex: can't poison undead), further adjust attack effects
  • Each effect that has passed the check is applied to defender

Repeat for counter attack.

What I'm concerned about: the method mutates attack instance, meaning any time I ask a character for attack, I actually need to get a copy of the attack. My attack class has started as a tuple, and there's already some code around that expects that checking attack is a simple operation. If attack gets overloaded with effects, and starts to be passed around as copy, this may have implications on my other code.

\$\endgroup\$
  • 2
    \$\begingroup\$ I would strongly advise against handling poison and time delay stuff in your attack methods. Generally these kinds of buffs you listed happen on their own time intervals. For example poison happens applies itself every x seconds (normally) and not when you attack, so you should handle buffs/debuffs like this in your update functions. Stuff like knockdown is instant and maybe you want to just lock input and skip functions until it's timer has elapsed. I'd suggest adding a enum to all your buff objects that tells you how to apply it (OnAttack, OnHit, OnUpdate, Instant, etc.) \$\endgroup\$ – Benjamin Danger Johnson Jan 7 '14 at 22:22
  • \$\begingroup\$ Right, forgot to mention this is turn based RPG. So poison over time is applied by attack. The poison damage is applied at the start of each turn \$\endgroup\$ – Alex Stone Jan 7 '14 at 22:58
2
\$\begingroup\$

You do not need nor what attack to be mutable. There's easy workarounds to avoid it.

The attack has a list of AttackEffects (or whatever you want to name them). The recipient's OnAttacked method takes that list of effects as an immutable reference (no need to copy). The method then iterates over each effect and applies that effect to the target with e.g. an OnAttackDamage or OnPoisoned method which takes input relevant parameters (damage type, strength, time, etc.). This method then checks the list of buffs (or a more efficient set of cached data) and modifies the input values accordingly before applying them.

Your attack does not do damage over time. The attack causes the recipient to acquire a "debuff" that does poison over time. The attack is a once-off affair. There is simply a PoisonAttackEffect subclass that includes the poison damage and time. The recipient's OnPoisoned takes that damage and time and creates a new PoisonDebuff to add to the character with the given parameters, modified as appropriate by resistances and the like.

That's the crux of Benjamin's comment above. The attack is always instant. There is no part of the attack that is not instant. Lasting effects of the attack are handled by having the attack instantaneously insert a record into the target of the damage to be done over time.

In somewhat rather overly-typed pseudo-code, it might look something like:

// Effects that weapons can have

class AttackEffect:
  abstract Apply(target)

class DamageEffect:
  int amount
  override Apply(target):
    target.OnDamage(this.amount)

class PoisonEffect:
  int amount
  int seconds
  override Apply(target):
    target.OnPoison(this.amount, this.seconds)

class Weapon:
  AttackEffect effects[]
  void Attack(target):
    for effect in this.effects:
      effect.Apply(target)

class Character:
  Buff buffs[]

  OnDamage(amount):
    for buff in this.buffs:
      amount = buff.ModifyDamage(amount)

    hp -= amount
    CheckIfDead()

  OnPoison(amount, seconds):
    for buff in this.buffs:
      amount, seconds = buff.ModifyPoison(amount, seconds)

    this.buffs.append(new PoisonDebuff(amount, seconds))

  Update(dt):
    for buff in this.buffs:
      buff.ApplyUpdate(this, dt)

    RemoveExpiredBuffs(this.buffs)

You can take this a bit further by allowing the attacker to have buffs that grant additional AttackEffects beyond those innate to the weapon or to have the attacker's buffs be allowed to modify the "outgoing" values (so e.g. the attacker could have a buff like "+50% to all poison damage").

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.