4
\$\begingroup\$

I am making a data drive system for creatures, abilities, items, etc. Considering my ability objects I need to create "templates" from the these since parsing them in real time is too slow. I am trying to find a way to just reference these on my creatures instead of a copy of the reference for each ability.

The thing is I have changeable data in the ability objects that are referenced. This is because the XML data can contain data wherefore no fields exist.

Take a normal attack ability.

<ability name="Attack">
    <!-- Owners weapon range-->
    <range>Weapon</range>
    <!-- for ai FSM to find a proper thing to do.
    <type>damage</type>

    <!-- bitflags a attack needs a target so before this ability starts the player needs to select one trough GUI -->
    <behaviors>
        <behavior>CREATURE_TARGET</behavior>
        <behavior>ATTACK</behavior>
    </behaviors>

    <!-- bitflags a attack obviously needs a melee weapon
    If it would also contain AXE and TWO_HANDED  it would require a two handed axe to activate-->
    <requirements>
        <requirement>MELEE_WEAPON</requirement>
    </requirements>

    <!-- I have a turn/tickbased game -->
    <tickCost>WeaponSpeed</tickCost>
    <!-- basic stats, could also go into special parameters -->
    <cooldown>0</cooldown>
    <manaCost>0</manaCost>
    <energyCost>0</energyCost>
    <lifeCost>0</lifeCost>

    <!-- Actions associated with this ability -->
    <abilityActions>
        <!-- the event on which the containing actions are triggered -->
        <event type="OnAbilityActivated">
            <actions>
                <!-- the action itself, this creates a `new DamagaAction(..)`-->
                <action>                        
                    <class>Damage</class>
                    <damageType>PHYSICAL</damageType>
                    <target>TARGET</target>
                    <damage>Weapon</damage>
                </action>
            </actions>
        </event>
    </abilityActions>
</ability>

A ability could hold as many actions as you want. It could throw 100 spikes where each spike would have a different visual and status effect.

Now let's imagine someone creates a creature with stamina, there is nothing to hold stamina so we need additional parameters. Imagine you want to create a ability that damages this new creatures stamina.

<parameter name="stamina_damage" value"10"></parameter>
                <action>                        
                    <class>Damage</class>
                    <damageType>PHYSICAL</damageType>
                    <target>TARGET</target>
                    <damage>stamina_damage</damage> <!-- parameter reference -->
                </action>

I could also store display information into the parameter to display proper info about the ability.

The actual code

The following code is a clean example of what I am currently trying to do.

public class Ability implements CreatureListener {    
    private final String name;//Not actually final since that would make my constructor too long with parsing everything. Considering on a builder pattern for this.
    //...

    //This gets changed and used by each creature that has this reference
    public HashMap<String, Object> parameters = new HashMap<>();

    public Ability(XmlNode abilityNode)
    {
         parseAbility(abilityNode);
    }

    public void startAbility(Creature owner)
    {
        parameters.put("OWNER", owner);
        //Perform needed actions, if they need the owner they can take it from parameters

        //foreach action that is tagged OnStartAbility -> action.perform();
    }

    @Override
    public void onAttack(Creature owner, Creature target) {
        parameters.put("OWNER", owner);
        parameters.put("TARGET", target);

        //Perform needed actions, if they need the owner or target they can take it from parameters

        //foreach action that is tagged OnAttack -> action.perform();
    }

    @Override
    public void onTakeDamage(Creature owner, Creature attacker, int damageDealt) {
        parameters.put("OWNER", owner);
        parameters.put("ATTACKER", attacker);
        parameters.put("DAMAGE_DEALT", damageDealt);

        //Perform needed actions, if they need the owner or attacker they can take it from parameters

        //foreach action that is tagged OnTakeDamage -> action.perform();
    }
}

Each time a certain ability is run the possible parameters are being re-set. Consider the following scenario:

  1. Attacking creature does AOE action and finds two victims.
  2. Attacking creatures uses a damage action to calculate the damage and apply to victim1.
  3. Victem1 triggers onTakeDamage(..) it sets itself as it's OWNER, attacking creature as TARGET and the damage it took as DAMAGE_DEALT

  4. Victem1 uses a damage action to apply DAMAGE_DEALT to ATTACKER. Attacking creature uses a damage action to apply damage to victim2.

  5. Victem2 triggers onTakeDamage(..) it sets itself as it's OWNER, attacking creature as TARGET and the damage it took as DAMAGE_DEALT
  6. Victem2 uses a damage action to apply DAMAGE_DEALT to ATTACKER.

This seems safe to me but is it really? And this seems to be considered very bad practice. So what are my options?

EDIT I created another ability that should do damage to a single target and heal nearby friendly units on success. It should look something like this and I did my best to document it.

<ability name="DefineSubtraction">
            <range>use_range</range> <!-- corrsponds to parameter -->
            <AI><!-- for AI to decide what to do -->
                <abilityType>
                    <type>SINGLE_DAMAGE</type>
                    <type>MULTIPLE_HEAL</type>
                </abilityType>
            </AI>

            <!-- The behavior of the skill -->
            <behaviors>
                <behavior>CREATURE_TARGET</behavior> <!-- Creature need to select a target on activation -->
                <behavior>MAGIC</behavior> <!-- Cannot target magic imune and does not trigger PhysicalDamageTaken -->
                <!-- More things like NO_TARGET, COORDINATE_TARGET, PASSIVE, SUSTAINED can be added and they act like bits in a bitset -->
            </behaviors>

            <requirements><!-- Requirements to use this ability also creates a bitset from the combinations -->
                <requirement>SPELL_BOOK</requirement> <!-- Need a spell book -->
                <!-- Could have many requirements like AXE + TWO_HANDED would require 2 handed axe and MELEE_WEAPON + SINGLE_HANDED requires single handed melee weapon -->
            </requirements>

            <!-- The parameters, this actually is the thing I'm worried about. As you can see I need this here, especially when modders want to introduce new creatures with different stats and all.
             It will also hold additional information to show information about the ability. I also need this to add additional data at runtime, like when I need current data of the owner. -->
            <parameters>
                <parameter type="int">
                    <name>use_range</name>
                    <value>10</value>
                </parameter>
                <parameter type="int">
                    <name>spell_damage</name>
                    <value>20</value>
                </parameter>
                <parameter type="int">
                    <name>heal_radius</name>
                    <value>200</value>
                </parameter>
            </parameters>

            <!-- Basic stats, not really sure what I exactly need. I probably put all this in parameters eventually -->
            <tickCost>WeaponSpeed</tickCost>
            <cooldown>0</cooldown>
            <manaCost>0</manaCost>
            <energyCost>0</energyCost>
            <lifeCost>0</lifeCost>

            <!-- The actual action blocks for the ability start here.
             For each phase you can add abilities, there are many phases like:
             OnDamageTaken, OnAbilitySuccess, OnDeath, OnEquip, etc, etc. -->
            <abilityActions>
                <event type="OnAbilityActivated"><!-- Actions that trigger when ability is activated -->
                    <actions>
                        <!-- The actions in order of execution. Actions can force everything to stop until finished or just run in one go by default.
                         Actions are essentially classes that get instantiated for this ability. So these are hardcoded and run by the ability, the actions
                         handle everything what happens in the game world. I could even hook actions up to map objects. Some actions are: Damage, Projectile,
                          PlaySound, Move, Effect, AOE and many more. But just with those 4 I can make most abilities of any game. I keep them very basic
                          Damage just does damage and might trigger onDamageTaken. AOE just fills a list with participants for the next action in line to use.-->
                        <action>
                            <!-- regular damage action -->
                            <class>Damage</class>
                            <!-- Damage type -->
                            <damageType>MAGICAL</damageType>
                            <!-- The target, since the behavior is CREATURE_TARGET I know it has been filled when ability is activated -->
                            <target>TARGET</target>
                            <!-- amount of damage from parameters -->
                            <damage>Spell_Damage</damage>
                        </action>
                    </actions>
                </event>

                <event type="OnAbilitySuccess"><!-- Actions that trigger when a initial ability was successfully, for example landing a attack although OnAttackLanded is another Phase that can be triggered  -->
                    <actions>
                        <!-- here I queue two actions up, I can essentially create hundreds of actions here that all behave differently. -->
                        <action>
                            <!-- puts all creatures found in the parameters -->
                            <class>AOE</class>
                            <!-- bitset to determine what creatures to add -->
                            <target_type>
                                <type>FRIENDLY</type>
                                <type>NEUTRAL</type>
                                <type>ALLIED</type>
                            </target_type>
                            <!-- point from where to draw circle of influence -->
                            <origin>OWNER_ORIGIN</origin>
                            <!-- radius of circle of influence -->
                            <radius>heal_radius</radius>
                        </action>

                        <action>
                            <!-- action that heals -->
                            <class>Heal</class>
                            <!-- Target, since AOE object always stores crteature in map as "TARGETS", creatureSet I can just put TARGETS here-->
                            <target>TARGETS</target>
                            <!-- Amount to be healed, from parameters -->
                            <damage>HealAmount</damage>
                        </action>
                    </actions>
                </event>
            </abilityActions>
        </ability>

As you can see, the system is very versatile and I think I can get it working with that HashMap<String, Object> parameters, well my prototype is working pretty good at the least but it's pretty basic too.

\$\endgroup\$
3
  • \$\begingroup\$ It seems like you're trying to be overly generic. You're trying to define data structures, values and logic in XML only. This is not going to get you far because it will quickly become extremely complex. Do you really have to declare so many things in XML? How about defining a set of interfaces and have one implementation created for each unique action &c.? \$\endgroup\$
    – JimmyB
    Mar 23, 2016 at 16:33
  • \$\begingroup\$ @HannoBinder The thing is, a action like damage can be used in so many ways. Yes I could have many implementations of Damage. I think it won't get complicated at all, in my code I use a lot of inner classes that define the data. If let's say I need to parse the behavior block I just need to sent it to a method in that inner class and get my bitset. If I want to know if the set contains ATTACK I just ask for it. It's just that some people find it very bad practice to alter that parameter map when it's object is referenced by many other objects. So I'm looking for alternatives. \$\endgroup\$
    – Madmenyo
    Mar 23, 2016 at 16:58
  • \$\begingroup\$ @MennoGouw Whoops, sorry. My mistake. \$\endgroup\$ Mar 23, 2016 at 17:50

1 Answer 1

3
\$\begingroup\$
public interface OneOnOneAction<S extends Creature, T extends Creature, P> {
  public void perform(S sourceCreature, T targetCreature, P parameter);
}


public interface Creature {
  // ...
}

public interface StaminaCreature extends Creature {
  public int getStamina();
  public void setStamina(int stamina);
}


public class TransferStaminaAction implements OneOnOneAction<StaminaCreature, StaminaCreature, Integer> {

  public void perform(StaminaCreature sourceCreature, StaminaCreature targetCreature, Integer parameter) {
    sourceCreature.setStamina( sourceCreature.setStamina() - parameter);
    targetCreature.setStamina( targetCreature.getStamina() + parameter );
  }

}

You can use reflection to build creatures, actions &c. from configuration like

<action id="transferStaminaAction" class="my.game.TransferStaminaAction"/>
<creature id="staminaMan" class="my.game.StaminaManCreature">
  <actions>
    <actionref id="transferStaminaAction"/>
  </actions>
</creature>

This is just an example to illustrate how behavior can be parameterized without having to invent a Touring-complete language for configuration.

I think however that we don't have enough information on what you're trying to achieve in the end to give more useful advice.

\$\endgroup\$
3
  • \$\begingroup\$ Interesting, I will be trying this out on my prototype. I have added another possible ability in XML form. I eventually want to extend these abilities to be able to add modifiers to other entities as well, modifiers can hold these same actions as well. Like a bleed ability that needs to be attached to another player BUT still needs to know what creature it came from to notify it. \$\endgroup\$
    – Madmenyo
    Mar 23, 2016 at 17:06
  • \$\begingroup\$ My ultimate goal is to have a extremely versatile system where you can essentially create every ability that is needed by just parsing XML. By achieving this it's just a small to create a tool that even my dog can use to create dozens of abilities. \$\endgroup\$
    – Madmenyo
    Mar 23, 2016 at 17:08
  • \$\begingroup\$ Been thinking about this, but would there be anything wrong with just passing parameters to the actions and perhaps the owner although that can go as "OWNER" as well? I now have Target as a List<Creature> since a action prior could have chosen multiple targets, like a AOE or line of fire action. I now see it is indeed better to just suplly the action classes with the needed data instead of changing the "template" ability and falling back on that. The ability would just supply the parameters pass by the XML and additional parameters depending on the listener method. \$\endgroup\$
    – Madmenyo
    Mar 24, 2016 at 12:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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