Hello,

(Should this be in Support? As I don't need support with ServUO per say, I posted here - casual discussion.)

I am programming a system where players can unlock various abilities. I get around the code pretty easily, but I don't have much experience when it comes to building systems that are scalable and well thought-out. I'd like to get the community's input if my direction makes sense or am I headed in a wall.

The abilities will be very varied :
  • Weapon abilities like OSI (AI, Whirlwind, Double Strike, etc.)
  • Spell abilities (damage/speed/mana cost enhancements, AoE enhancements with special effects, etc.)
  • Weapon and Spell passives that can be toggled for various OnHit/OnMiss/OnBeingHit/etc. effects
  • Monster special abilities
  • Probably more varied interactions as I flesh out the system
I didn't like how for existing abilities (Armore Ignore for example), you need to go into various bits of codes to understand its full effect. I remodeled my system to have the full effects more centralized with an "Action" interface which has generic methods such as :
  • OnBeforeSwing
  • OnSwing
  • OnHit
  • OnMiss
  • GetDamageIncreasePerct()
  • GetFlatDamageBonus()
I created a BaseAction which returns the default no bonus for all virtual methods to be overridden in specific actions.

I created a BaseWeaponAction, from which abilities like Armor Ignore inherit.
I will create a BaseSpellAction.

I made my own CustomBaseWeapon inheriting BaseWeapon overriding the existing OnHit function as I don't want to deal with all the OSI stuff. It calls the player's current active action's methods for the bonuses to take into account when calculating the damage to apply:
C#:
IAction action = atker_PM.ActiveAction;
bonusDamageFlat += action.BonusDamageFlat();
bonusDamagePerct += action.BonusDamagePerct();
bonusArmoreIgnorePerct += action.BonusArmoreIgnorePerct();
bonusHitchancePerct += action.BonusHitChancePerct();
action.OnHit(attacker, defender);

If I take my version of Armor Ignore as an example, my class ArmorIgnoreAction has the following methods :
C#:
        public override double BonusArmoreIgnorePerct()
        {
            return 100;
        }

        public override void OnHit(Mobile attacker, Mobile defender)
        {
            UseAction(); //Triggers cooldowns, usage costs, etc.

            attacker.SendLocalizedMessage(1060076); // Your attack penetrates their armor!
            defender.SendLocalizedMessage(1060077); // The blow penetrated your armor!

            defender.PlaySound(0x56);
            defender.FixedParticles(0x3728, 200, 25, 9942, EffectLayer.Waist);
            return;
        }

So far this architecture seems to be OK with what I foresee. Here are what I think may be a problem and would like second opinions :
  1. The cooldowns of my abilities are handled with Callback methods : is this an issue or a bad practice? I never worked with callbacks and not sure if I should use a "LastUse" tick count and compare periodically instead.
  2. The way the damage calculation is done right now, I have to add a method for each and every bonus I wanna use in my damage calculation, and for each bonus source. One work around I see is creating a "Damage" class that can be passed to the current ability's OnHit method (and any other bonus sources). Calling one method per sources instead of one line per damage stat per sources.
I understand I didn't post the full code as I tried to keep it synthetized, but I would be happy to share more if more is needed to contribute to the discussion.

Thanks for sharing your expertise!
 
Be careful with making instances of class in combat calculation. It doesn't matter in most of the IT systems, but ServUO is game server emulator :)

If each hit will generate instance of class, it can put pressure on the garbage collector.

However if you have a limited amount of actions and reuse them, then it shouldn't be a problem.
 
I went ahead and created a "Damage" class, but as you say one instance is created each hit so now I'm doubtful! Should I avoid this altogether, or is there a way to "force" the garbage collect of the class once the damage has been dealt?

Would a struct be a better idea? One that get passed around the various modifiers, passed to a static DamageCalculation class for applying the damage. At this point, I'm not sure what's the difference between a struct + static class and a class with a struct property and static methods. I'll do some googling :)

Thanks for the heads-up, exactly the kind of warning I was looking for!
 
is there a way to "force" the garbage collect of the class once the damage has been dealt?
There is no way to control garbage collection. (Okay there is a way, but full of unsafe code based on undefined behavior.)

Would a struct be a better idea?
You can avoid garbage collection by using structs. They are usually allocated on stack, which is really fast.

class with a struct property
I've said usually, unfortunately struct as a class property is allocated on heap. Same if you cast struct into an interface.

Should I avoid this altogether
I'm not saying you should avoid classes. Just in some performance critical code, which damage calculation could possibly be.
 
I did some Googling and I believe you're right, it's a vector to get things out of control if I create an instance of a class for each damage dealt.
I also got to learn about the stack and the heap...which I wasn't aware of.

So I believe I'll change my code to use a struct. The struct will get passed around and a static damage class will concentrate all data calculation. I'll see where this goes.

Thanks again.
 
Back