Clamping values in a Javascript object (redux)

0 votes
asked Sep 10 by BreezeIndigo (120 points)

With reference to this old post: https://twinery.org/forum/discussion/1889/most-efficient-way-to-clamp-number-in-an-advanced-javascript-object-sugarcube

I want to do something similar (in Twine2, Sugarcube 2.18.0), i.e. create an object with stats that I can clamp and I'm using the third method described:

// Actor constructor
window.Actor = function (obj) {
	// Setup your data members here
	this._data = {
		FirstName   : "",
		LastName    : "",
		Description : "",
		SomeStat    : ""
	};

	// Merge properties from obj, if any
	if (typeof obj === "object") {
		Object.keys(obj).forEach(function (p) {
			this[p] = obj[p];
		}, this);
	}
};

// Actor prototype
Object.defineProperties(window.Actor.prototype, {
	// Serialization method
	"toJSON" : {
		value : function () { return JSON.reviveWrapper('new Actor(' + JSON.stringify(this._data) + ')'); }
	},

	// Getter/Setter methods
	"FirstName" : {
		get : function() { return this._data.FirstName; },
		set : function(val) { this._data.FirstName = val; }
	},
	"LastName" : {
		get : function() { return this._data.LastName; },
		set : function(val) { this._data.LastName = val; }
	},
	"FullName" : {
		get : function() { return this._data.FirstName + " " + this._data.LastName; }
	},
	"Description" : {
		get : function() { return this._data.Description; },
		set : function(val) { this._data.Description = val; }
	},
	"SomeStat" : {
		get : function() { return this._data.SomeStat; },
		set : function(val) { this._data.SomeStat = Math.clamp(val, 0, 100); }
	}
});

// Actor static (non-instance) methods
window.Actor.Debug = function (Who) {
	return "Hello, my name is " + Who.FullName + ".  You killed my father.  Prepare to die.";
};

The difference is, the actor I'm constructing has two sets of stats, so my constructor looks a bit like this:

window.Hero = function (obj) {
	this.name = "";
	this.race = "";
	this.titles = [];
	this.stats = {
		strength : "",
		intelligence : "",
		stamina : ""
	};
	
	this.equipment = {
		weapon : [weaponName, weaponDmg],
		armor : [armorName, armorAC]
	}
};

Object.defineProperties(window.Hero.prototype, {
	"toJSON" : {
		value : function() { return JSON.reviveWrapper('new Hero(this.name, this.race, this.titles, ' + JSON.stringify(this.stats) + ', ' + JSON.stringify(this.equipment) + ')'); }
	},
	
	"strength" : {
		get : function() {
			return this.stats.strength;
		},
		set : function(val) {
			this.stats.strength = Math.clamp(val, 0, 100);
		}
	},
	
	...etc
	
	armor : {
		get : function() {
			return this.equipment.armor;
		},
		set : function(val) {
			this.equipment.armor[1] = Math.clamp(val, 0, 100);
		}
	}
});

The problem is that this doesn't actually clamp the values properly. I also don't know if this will handle the second lot of stats, which are stored as an array of values. I have to admit I only vaguely understand this code so my clumsy copying probably bunged something. Help us, Obi-wan.

1 Answer

0 votes
answered Sep 10 by TheMadExile (9,000 points)

The problem is that this doesn't actually clamp the values properly.

Which means what, exactly?  How are you using it and what is happening?

TIP: When something isn't working either the way you want or think it should, always provide details and examples of how you're using it and what it is doing along with any other information.

 

First.  The <Hero>.stats object in its constructor should look like the following:

	this.stats = {
		strength     : 0,
		intelligence : 0,
		stamina      : 0
	};

In other words, if they're supposed to be numbers, then initialize them as numbers in the first place.

 

Second.  Are you doing something like the following?

/* Assume $myHero is an instance of Hero. */
<<<set $myHero.stats.strength to 50>>

If so, then you should be doing the following instead:

/* Assume $myHero is an instance of Hero. */
<<<set $myHero.strength to 50>>

The reason is because the strength getter and setter are defined on instances of Hero, not its stats object.

 

I also don't know if this will handle the second lot of stats, which are stored as an array of values. I have to admit I only vaguely understand this code so my clumsy copying probably bunged something.

I've got some other things to say about this, but no time to say them now, so I'll be back later.

 

commented Sep 11 by BreezeIndigo (120 points)

Which means what, exactly?  How are you using it and what is happening?

TIP: When something isn't working either the way you want or think it should, always provide details and examples of how you're using it and what it is doing along with any other information.

Apologies.

The values do not clamp. I use a <<set>> and it seems to ignore the clamped ranges.

Second.  Are you doing something like the following?

Uh...yes. Oops. That seems to have fixed it.

Incidentally, your <<set>> method has <<< instead of << at the start - is that just a typo?

I've got some other things to say about this, but no time to say them now, so I'll be back later.

This sounds ominous.

Say however I want to track a long series of stats with predictable properties, e.g. generating a random Hero object:

window.Hero = function (obj) {
	this.name = "";
	this.race = "";
	this.level = 0;
	this.stats = {
		strength : [0, 1, 2], 
		dexterity : [0, 1, 2],
		someStat : [0, 1, 2],
		someOtherStat : [0, 1, 2],
		bladdercapacity : [0, 1, 2]
		
		...etc
		
	}
});

...where the array would represent (for example) a minimum value, maximum value and level modifier which I could use to generate a random hero with stats within a range. Would it then be possible to clamp those ranges?

I hope that better clarifies what I'm attempting to do. Sorry if this is a bit of a vague example, I haven't really got anything solid written as yet, I'm mostly just trying to explore what's possible so I can design around it.

...