Reputation: 201
I am not quite sure if the wording in the title accurately describes what I am looking to do, allow me to explain:
Suppose in a game, you get different type of points from leveling up. But the amount of points of each type you get each level can be arbitrary.
For example,
I get 3 offensive
point for every 2 level.
But I get 2 defensive
point at level 2, 4, 5, and 6 say.
And perhaps 1 supportive
point at every level except the first.
Now here's what I've done:
//Suppose I have my hero
var Sylin = new Hero();
Sylin.Level = 5;
//My goal is then to set
// Sylin.OffensivePoint to 6
// Sylin.DefensivePoint to 6
// Sylin.SupportivePoint to 4
Sylin.prototype.OffensivePoint = function() {
return 3*Math.floor(this.Level/2);
};
Sylin.prototype.DefensivePoint = function() {
var defPoint = 0;
for(var i=2; i <= this.Level; i++) {
//maximum level being 6
if(i==2 || i >= 4) {
defPoint += 2;
}
}
return defPoint;
};
Sylin.prototype.SupportivePoint = function() {
return this.Level - 1;
};
It's all fine and dandy, but if the maximum level is extended, the points lists will be updated and then it gets really clumsy, especially if I have things like:
2 points every 3 level, but 3 point on the 9th and 13th level or something apparently lacking in pattern so I can't always do it like what I have for OffensivePoint()
.
What I have in mind for this type of problems in general is a structure like so:
Level TotalPoint
. . 1 . . . . . a
. . 2 . . . . . b
. . 3 . . . . . c
. . 4 . . . . . d
. . 5 . . . . . e
and so on until the maximum level
In the code, I could then perhaps do:
Sylin.prototype.talentPoint = function() {
return readTalentPointTable(this.Level); //?
};
But then this can still get quite convoluted if there's 20 level with 5 different types of points you can get, say :/
.
.
EDIT
Ok, so I could do something like:
var OffensivePointTable = [0,0,2,2,4,6,8,8,8,10,12];
function getOffensivePoint(level) {
return OffensivePointTable[level];
}
Would it be easier if I store the data by the level in which a point is increased, or by the running total as above?
.
.
EDIT 2
Ok, can I perhaps reverse the order of the structure to look at the type first, then level?
var theTable = {
o: [0,1,0,1,1,0,0,0],
d: [0,0,2,0,2,0,2,0],
s: [0,1,2,3,4,5,6,7]}
//then your CalculateStats:
Sylin.prototype.CalculateStats = function() {
this.offensivePoint = 0;
for(var i=1; i<= this.Level; i++) {
this.offensivePoint += theTable[o][i];
}
}
Upvotes: 0
Views: 126
Reputation: 43728
Sure you could always create an hardcoded array of all points, however you could also simply hardcode the exceptions and stick with an algorithm when you can.
Just an idea... that would require your hero to keep track of his points instead of recompiling them dynamically however, but that's probably a good thing.
//have a map for exceptions
var pointExceptionsMap = {
'9': {
off: 3 //3 points of offense on level 9
}
};
Sylin.prototype.levelUp = function () {
var ex = pointExceptionsMap[++this.level];
//update offense points (should be in another function)
this.offense += (ex && typeof ex.off === 'number')?
ex.o /*exception points*/:
this.level % 2? 0 : 2; //2 points every 2 levels
};
Then to level up, you do hero.levelUp()
and to get the points hero.offense
. I haven't tested anything, but that's the idea. However, if you require to be able to set the level directly, you could either have a setLevel
function that would call levelUp
the right amount of times but, you would have to use a modifier to allow you leveling down as well.
You could also use my current idea and find an efficient way of implementing exceptionnal algorithms. For instance, you could still dynamically compile the number of offense points, and then add or remove points from that result based on exceptions. So if you need 2 points every 2 levels, but 3 for the level 9, that means adding 1 additionnal point to the compiled points. However, since when you reach higher levels, you wan to retain that exception, you would have to keep track of all added exception points as well.
EDIT: Also, nothing prevents you from using a function as a new exceptionnal algorithm instead of a simple number and if you plan to make the algorithms configurable, you can simply allow users to override the defaults. For instance, you could have a public updateOffense
function that encapsulates the logic, so that it can be overriden. That would be something similar to the Strategy design pattern.
EDIT2: Here's a complete example of what I was trying to explain, hope it helps!
var Hero = (function () {
function Hero() {
this.level = 0;
this.stats = {
off: 1,
def: 0
};
}
Hero.prototype = {
statsExceptions: {
'3': {
off: 3 //get 3 points
},
'6': {
def: function () {
//some algorithm, here we just return 4 def points
return 4;
}
}
},
levelUp: function () {
++this.level;
updateStats.call(this, 1);
},
levelDown: function () {
updateStats.call(this, -1);
--this.level;
},
setLevel: function (level) {
var levelFn = 'level' + (this.level < level? 'Up' : 'Down');
while (this.level !== level) {
this[levelFn]();
}
},
statsFns: {
off: function () {
return (this.level % 2? 0 : 2);
},
def: function () {
return 1;
}
}
};
function updateStats(modifier) {
var stats = this.stats,
fns = this.statsFns,
exs = this.statsExceptions,
level = this.level,
k, ex, exType;
for (k in stats) {
if (stats.hasOwnProperty(k)) {
ex = exs[level];
ex = ex? ex[k] : void(0);
exType = typeof ex;
stats[k] += (exType === 'undefined'?
/*no exception*/
fns[k].call(this) :
/*exception*/
exType === 'function' ? ex.call(this) : ex) * modifier;
}
}
}
return Hero;
})();
var h = new Hero();
console.log(h.stats);
h.setLevel(6);
console.log(h.stats);
h.setLevel(0);
console.log(h.stats);
h.levelUp();
console.log(h.stats);
//create another type of Hero, with other rules
function ChuckNorris() {
Hero.call(this); //call parent constructor
}
ChuckNorris.prototype = Object.create(Hero.prototype);
//Chuck gets 200 offense points per level y default
ChuckNorris.prototype.statsFns.off = function () {
return 200;
};
//Not exceptions for him!
ChuckNorris.prototype.statsExceptions = {};
console.info('Chuck is coming!');
var c = new ChuckNorris();
c.setLevel(10);
console.log(c.stats);
Upvotes: 1
Reputation: 4193
You could use an object to store the amount of points to increment at each table (I didn't use your exact numbers, but you get the idea):
var LevelPoints = {
'1': {
o: 1,
d: 2,
s: 1
}
'2': {
o: 3,
d: 1,
s: 1
}
'3': {
o: 1,
d: 1,
s: 0
}
'4': {
o: 2,
d: 3,
s: 1
}
//etc.
}
For example, to access the offensive point increase at level 2, use LevelPoints['2'].o
.
This requires a lot of typing I suppose, but sometimes just having all the data there makes things easier. Making your code readable to you and easy to change is always nice. It's also useful as a quick reference—if you're wondering how many offensive points will be gained at level 6, you can know immediately. No need to decipher any procedural code. Of course, this is personal preference. Procedural approaches are faster and use less memory, so it's up to you whether that's worth it. In this case the difference will be negligible, so I recommend the data-driven approach.
Also, note that I used var
to set this object. Because it can be used by all instances of the Sylin
constructor, setting it as an instance variable (using this
) is wasteful, as it will create the object for every instance of Sylin
. Using var
lets them all share it, saving memory.
Alternately, you could store the running total at each level, but IMO this requires more effort for no good reason. It would take less of your time to write a function:
Sylin.prototype.CalculateStats = function() {
this.OffensivePoint = 0;
this.DefensivePoint = 0;
this.SupportivePoint = 0;
for (var i = 1; i <= this.Level; i++) {
this.OffensivePoint += LevelPoints[i].o;
this.DefensivePoint += LevelPoints[i].d;
this.SupportivePoint += LevelPoints[i].s;
}
}
Then just run this function any time the user changes the level of the character. No need to pass the level, as the function will already have access to the this.Level
variable.
Upvotes: 1
Reputation: 6668
Why not store the points in an array of objects -
var pointsTable = [{offensivePionts: 1, defensivePoints: 1}, {offensivePoints: 1, defensivePoints: 2}]; //extend for any level
And then just get return points by referencing the corrent property -
function getOffensivePoints(level) {
return pointsTable[level]['offensivePoints'];
}
You can easily extend the datastructure with methods like addLevel etc.
Upvotes: 1