Skip to main content
GameDev.net gamedev.net
🔒 Locked

static properties and inheritance

Started by Ezbez Aug 25, 2007 at 5:20 AM 2 replies 1.5k views
Original Post
Ezbez
Ezbez
I've got an issue with this when I try to use static readonly tables for things such as BAB and saves. The idea was to have a base, abstract class Class that Fighter, Wizard, etc. inherit from. Since all Classes need BAB and saves and they are calculated the same way regardless of Class (looking them up in the table based on current level in that class), I wanted to put it all in Class, rather than in the base classes. This failed miserably, since each (character) class needs to have it's own static readonly tables, but then Class can't access them. I think you'll see what I want to do when you see some code:
	abstract class Class : IClass
	{
		// Tabular data:
		static readonly int[] BAB;
		static readonly int[] WillSaves;
		static readonly int[] ReflSaves;
		static readonly int[] FortSaves;

		int level;

		public int BaseAttackBonus
		{
			get
			{
				return BAB[level - 1];
			}
		}

		public int WillSave
		{
			get
			{
				return WillSaves[level - 1];
			}
		}
		public int FortSave
		{
			get
			{
				return FortSaves[level - 1];
			}
		}
		public int ReflSave
		{
			get
			{
				return ReflSaves[level - 1];
			}
		}

		public abstract int HitDie
		{
			get;
		}

		public Class()
		{
			level = 1;
		}

		public virtual void LevelUp()
		{
			level += 1;
		}
	}

	class TabularData
	{
		// Defines basic stats that many classes use and reuse

		public static readonly int[] LowSaves; // Slow progression of saves
		public static readonly int[] HighSaves; // Fast progression of saves
		public static readonly int[] CombatBAB; // For, say, a fighter
		public static readonly int[] SupportBAB; // For, say, a cleric
		public static readonly int[] CasterBAB; // For, say, a wizard

		static TabularData()
		{
			LowSaves = new int[] { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6 };
			HighSaves = new int[] { 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12 };
			CombatBAB = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
			SupportBAB = new int[] { 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15 };
			CasterBAB = new int[] { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10 };
		}

	}

	class Fighter : Class
	{
		static Fighter()
		{
			// Tabular information
			BAB = TabularData.CombatBAB; // World asplode!
			WillSaves = TabularData.LowSaves;  // Clearly I can't access these because of their readonly - but even if I could, it would change it for ALL classes, not just Fighter
			ReflSaves = TabularData.LowSaves;
			FortSaves = TabularData.HighSaves;
		}

		public Fighter() : base()
		{
		}

		public override int HitDie
		{
			get
			{
				return 10;
			}
		}
	}
I can work around this fairly easily, because I could just copy and paste the BaseAttackBonus, WillSave, FortSave, and ReflSave properties into each base class along with the tables. But... that's not satisfying and is a bit of a pain. And I could hack it together with some virtual functions, but I don't see any nice way to do that. What's a good solution?
SiCrane
SiCrane
Well, first off, as you've noticed, assigning to a readonly static base member in a derived class isn't happy. It also doesn't make sense because since all the classes share a base then assigning to the base class readonly tables would change the tables for all the classes. So one way to handle things is instead of using static tables in the base class, use non-static member variables.

Personally I would store this kind of tabular data in data files, not inside the code itself, but I don't think the workshop has covered file I/O yet, so that may not be something you would want to use.

Alternately, you can eliminate the tables entirely. The numbers in the tables for the SRD progressions1 follow strict mathematical formulas easily expressed in code. For level L, weak saves are always (L/3), good saves are always (2 + L/2), bad BAB is (L/2), average BAB is (L*3/4) and good BAB is L.2 This is following standard integral truncation rules.


1) Unfortunately some non-SRD classes have class numerical expansions that don't fit nicely into a mathematical formula, such as Malhalvoc Press' Arcana Unearthed.

2) This has lead to a number of players using house rules that allow for fractional accounting for multiclass characters. So a character with Bard 1/Wizard 1 would have a BAB of +1 rather than +0.
Ezbez
Ezbez
I think I'll go with the formula approach, since I don't really like having them as non-static members; the BAB, in my mind, belongs more to the class than to an instance of the class.

Quote:
Original post by SiCrane
2) This has lead to a number of players using house rules that allow for fractional accounting for multiclass characters. So a character with Bard 1/Wizard 1 would have a BAB of +1 rather than +0.


Clever! I might want to convince my DM to let our group do that.
SiCrane
SiCrane
If you do it, you might want to limit it to combining BABs. Since with fractional accounting a strong saving throw is +2.5 at first level, it makes multiclassing in classes with similar strong saving throws much more powerful.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.