RPG Style Weapons + Upgrades + Player Stat Upgrades

Hey all,

Developing a big RPG style inventory system. Currently it’s organized with class hierarchy:

Inventory (contains items, hotbar for quickselect, etc)

Item
->Weapon
->MeleeWeapon
->RangedWeapon
->Pistol
->Shotgun

etc…

So here’s what’s on my mind-grapes:

If a weapon can be upgraded (ex. purchase silencer for Beretta 9mm) through purchase of upgrades, and also ‘upgraded’ through player skill trees (ex. ‘+10% accuracy with pistols’), I was thinking that it might be best, on creation of the item, to poll the player for their skills and adjust the attributes accordingly and then to have an instance reference of a ‘WeaponUpgrade’ class which holds the internal (and importantly, specific to that instance of that weapon) upgrades, also adjusting the attributes.

Thoughts on this design implementation?

You could create one class in example the RangedWeapons package/folder that has a sub class that handles each weapon/upgrades/bonuses.

Or you could in that RangedWeapons package create multiple classes 1 for each weapon.
Explain a bit more on what you’re trying to do please?

The individual weapons are implemented already ie Beretta9mm extends Pistol, which extends RangedWeapon, so on and so forth back up to Item. The reason for this architecture is to allow a player to have any sort of item, be it a Pistol or a Potion, in their hot bar, and use it by clicking.

The question, or rather more of a implementation proposal since everyone does things their own way, is that I’m suggesting within the class Weapon:


public abstract class Weapon extends Item {
	protected double damage;
	protected double knockback;
	protected double speed;	//time (in seconds) for a full attack.
	protected double atkTime;
	
	protected double accuracy; //lower is more accurate
	protected double accuracyModifier;
        // etc.. with other attributes, plus:
        protected WeaponUpgrade upgrade;

And when the player stops in at the upgrade shop / other gameplay contrivance, the WeaponUpgrade is retrieved from the weapon, an attribute of it is modified, ex:


public void setAccuracyBonus(double d){
        this.accuracyBonus += d;
}

and off the player goes, now with a more accurate, reliable weapon.

When the accuracy (or damage, or knockback, or other attribute) of the weapon then needs to be retrieved, it can be grabbed as some calculation performed on:

  1. the weapon’s base attribute
  2. modifiers provided by specific upgrades to that instance of the weapon
  3. the player’s global benefits for weapons of that class (ie pistols)

If anything, I guess I’m asking if this seems like a reasonable way to go about doing things, or if there’s some glaring oversight I’m making.

That’s what i’d do, declare all the various upgrade/statistics variables of the UpgradeClass, than when a upgrade is clicked in the shop, set all the variables accordingly to the current level, and set the bonuses++ accordingly to the current upgrade bieng purchased.

Look for postings by Ray aka Bear in this thread:
http://groups.google.com/group/rec.games.roguelike.development/browse_thread/thread/68850017f706a5db

His post itemizes many things that you might want to consider for your item framework.

The general consensus is that class hierarchies are too restrictive when designing RPG entities. For example if you have a gun with a bayonet on it how will it fit into your hierarchy? I would recommend some kind of very flat framework or component-based system aka composition to give you the needed flexibility.

I think it would be “cleaner” to keep weapons and player skill upgrades clearly separated. I’d not have any player skill related variables in my Weapon class, and instead pass the PlayerSkills/UpgradeClass (whatever you call it xd) to the Weapon each fire()/update().

Right. The idea is that the Player Skills and Upgrades are 2 separate ideas. IE:


public void dealDamage(Sprite theOneGettingOwned){
    int dmg = 0;
    dmg += this.damage; //base weapon damage
    dmg *= this.upgrade.getDamageBonus(); //damage bonus from weapon upgrades
    dmg *= this.getOwner().getDamageBonus(this); //retrieve damage bonus from player for this weapon class

    theOneGettingOwned.receiveDamage(dmg);
}

What everyone else has been saying: the less mutation of state you do the better. Wielding a weapon shouldn’t modify a “damage” variable on the player, it should simply be included in a “calculateDamage()” method. RPGs can involve dozens of variables for calculating stats, but it’s something even the weakest computer nowadays will do in microseconds.

If you want to be fancy, then even an old standby like “hit points” is something you might want to calculate by keeping a list of individual wounds with their respective HP penalty, rather than just decrementing a variable. Then when a player applies the “first aid” skill, you can limit it to removing only wounds that his skill is sufficient to treat. Of course, if your game models a player’s health by way of a life bar, then by no means indulge in design overkill like that. Your code design should reflect the game you want

I just said something like this in a previous thread. What’s the difference between two ranged weapon? Some data…so you could skip on subclassing RangedWeapon. What’s the difference between a MeleeWeapon and a RangedWepon? Some data…so you could skip on subclass Weapon. The point to stop with this kind of thinking is (roughly) when a parent and child require different state data.

In my implementation MeleeWeapons and RangedWeapons hold different sorts of data and override methods in different ways, so the subclassing is relevant.

I think he meant that subclasses of RangedWeapon, like LongBow, CrossBow, Sling, etc aren’t useful subclasses. The behaviors of ranged and melee are different enough that they might deserve to be subclasses of Weapon, but individual cases of them aren’t really sufficient.

That brings us back to the “gun with a bayonet” problem though, and the answer is pretty simple: it’s two weapons. The identity of a single object shouldn’t be confused with the possibly multiple instances that may constitute its behavior. Similarly a robe with pouches could be armor and a bag. Rather than trying to fake multiple inheritance, you just accept that one item may actually be a composite of several objects.

This has implications for where you put unrelated properties like “sale value” and “durability” for example. The answer there may be you either create a CompositeItem for such chimeras, or you treat Weapon, Armor, and Bag as behaviors attached to Items, not subclasses of Item. I prefer the behavior angle, but there’s probably no single right answer there, it’s mostly a matter of what you’re most comfortable with and what tradeoffs you’re willing to make.

Yes. Exactly this. These things only vary by data. And remember that “code” can be considered data. Take a game like the original doom. All weapons could be used in exactly one way (bound to some input). The original Unreal? Each could be exactly used in two ways (by default bound to L&R mouse button). In the “classic” RPG “Dungeon Master” any item could be placed in the “weapon” hand and by clicking the GUI button the player could choose up to three different attacks depending on the item in question. In none of these examples does the “type” of weapon or what the kind of attack does effect how it’s triggered. Each just has more or less ways a given item can be used to perform an attack action.

Here’s a rough sketch of a possible design direction:



// concrete sub-classes of this deal with what a given attack "does" and the data (not specific to the given item)
// may be stored.
public abstract class OnAttack extends PerhapsSomeBaseActionClass
{
   // there could be some "bookkeeping" method here which deals with higher order logic
   // common to all attacks here, which in turn calls 'exec' or not depending on the 
   // overall design.

   public abstract ... exec(Enitity attacker, Item item, ....);
}

public class WeaponArchtypeForSomeGameWhichHasUpToTwoTypesOfAttackPerItem extends Whatever
{
  public OnAttack OnAttack1;
  public OnAttack OnAttack2;
}

public class OnProjectileAttack extends OnAttack
{
   // whatever fields are require to describe some set of projectile attacks

   public ... exec(Enitity attacker, Item item, ....)
   {
       // whatever stuff.
   }
}


So I think what you might be getting at with the data part of things (though I am rather ignoring your confusing Robe example), is that instead of :


public class M16 extends AutomaticRangedWeapon{
    public M16(Sprite owner){
        super(owner);
        this.damage = 3;
        this.accuracy = 10;
        // etc...
    }
}

Something like this might be more appropriate:


public class AutomaticRangedWeapon{
    public static enum AutoWpnType{
        M16, P90
    }

    public AutomaticRangedWeapon(Sprite owner, AutoWpnType type){
        super(owner);
        switch(AutoWpnType)
            case M16:
                this.damage = 3;
                this.accuracy = 10;
                //etc...
                break;
            case P90:
                this.damage = 2;
                this.accuracy = 15;
                //etc...
                break;
    }
}


[EDIT] to clarify, the reason that the Automatic part is specified is that upon use, it may have different attack behavior than a pistol (automatic fire, etc…) or a shotgun (shotgun may generate 8 attacks for spray).

If you approach this from the opposite direction you start with raw data stored in a common type e.g.:

HashMap<String, Object> m16 =
{
  "type" -> gun
  "automatic" -> true
  "damage" -> 16
  "accuracy" -> 10
}

HashMap<String, Object> p90 =
{
  "type" -> gun
  "automatic" -> true
  "damage" -> 2
  "accuracy" -> 15
}

HashMap<String, Object> knife =
{
  "type" -> blade
  "range" -> 1
  "damage" -> 3
  "accuracy" -> 15
}

Then toss these into a List.

Sometimes that’s all you’ll need and this form has the most amount of flexibility. If you have odd items you could change “type” -> Set(…).

From there introduce a class hierarchy where it makes sense. You could design your classes so that they just contain static methods that operate on the raw HashMaps thus decoupling your logic from the data.

Anyways, you avoid introducing classes too early which will only end up restricting you.

And here is an upgrade class (pseudocode):

class DamageUpgrade {
    static HashMap<String, Object> pimpMyWeapon(HashMap<String, Object> weapon) {
      HashMap<String, Object> modified = new HashMap<String, Object>(weapon);  // some kind of copy constructor
      modified("damage") += 10;
      return modified;  
    }
}

...
beefy_m16 = DamageUpgrade.pimpMyWeapon(m16);

Notice if you construct your class hierarchy properly then you could have a big list of “active upgrades” and then you just daisy chain the calculations to determine the final actual result to use in combat. You still have access to your original unmodified weapon too (m16).

How much you need to complicate things mostly depend on what kind of upgrades that you have. If you want to be able to add and remove “buffs” and modifiers then you’ll need to keep track of the base value of the weapon and either compute the value every update or when a weapon’s stat modifiers change. This is to avoid rounding errors, obviously assuming you’re using floating point variables for stats. I’d strongly recommend doing so though, since a 10% bonus to a 5 damage weapon is still 5 otherwise…

Exactly! With all entities represented as a simple data structure i.e. a HashMap, this becomes really easy. A damage buff/debuff is stored as a tuple of <String, Integer> e.g. “damage”, 10. Negative numbers are debuffs.

Store a list of them and calculate the final value only when you need it. Adding a buff to a weapon/item/mobile/anything is easy: just instantiate another tuple and add it to the list. Expiring buffs are a snap as well: just remove them from the list.

First of all, enums can have fields and constructors, so if you used them to store the stats, you can store them on the enum itself. However, you probably want to avoid this approach, because all instances of enums are staticly defined (which is largely the point of enums), so you would have to recompile your app to add a new weapon. What you’re going to want eventually is to read in some kind of config, like xml or json or .ini format, that builds a map of weapon definitions.

Personally I’d prefer keeping it out of config files to prevent users from adding their own weapon definitions, especially for a ‘campaign mode’ style single player.

You can always make java source code the config format, which will effectively freeze the config for whatever mode you’re using, but I still wouldn’t go as far as hardwiring them into an enum. Even a shooter will want to support mods.