OO where do entity type definitions go?

Started by
26 comments, last by Norman Barrows 9 years, 2 months ago

...

To continue this trend...
// object types
struct objtyperec
{
											// { The garbage that should not be in this struct starts here:
	// used by all objects
	char name[100];							// WHY IS THIS HERE? WHY IS THIS NOT A POINTER? That's 100 BYTES per structure you're forcing a fetch on.
	int wt,isliquid,						// nice naming. Totally useless. Why isn't isLiquid a boolean?
	// used by all weapons.
	iswpn,ishandwpn,dmg,wpnskill;			// Random. Again, booleans? Or a bitset? Or a flags variable?
	// used by missiles
	int rad;								// If it's used by the missiles, why is it here?
	float missilespeed;						// ^^^^^^^^^^^^^^^^
	// used by projectile launchers
	int ammotype,							// If it's used by the projectiles, why is it here?
	// used by foods
	foodboost,foodmoodboost,				// random, no type specified? Using , and newlines is A BAD IDEA.
	spoilchance, // dice(10000)				// Here for what reason?
	spoilrate,  							// ^
	// used for finding and making objects
											// Regarding the comment below... WHAT? TIME IN FRAMES? Don't track time by frames.
		// time in frames between findchecks  /   time in frames to make obj
	findtime,       						// Why is this here?
	
											// WHAT?
		// dice(10000) chance per findcheck   /   dice(10000) chance to make obj
	findchance,								// ... more garbage that doesn't belong here
		// skill that improves from finding   /   skill that improves from making
	findskill,      						// ... more garbage that doesn't belong here
	//used for cooking
											// ... more garbage that doesn't belong here
	fruit_reqd,meat_reqd,water_reqd,spices_reqd,fruitjuice_reqd,vegetables_reqd,nuts_reqd,grain_reqd,qtycooked,
	// used for making objects
											// ... more garbage that doesn't belong here
	numskills,numparts,numtools,skill[10],xp[10],part[10],partqty[10],tool[10],toolqty[10],
	// used for fishing
											// ... more garbage that doesn't belong here
	fishingbonus,
	// used for fixing stuff
											// ... more garbage that doesn't belong here
	canfix,									// ... more garbage that doesn't belong here
	numASB,   // # of additional skills boosted (above and beyond findskill)
	ASB[10],   //  list of additional skilltypes boosted
	value,      // trade value. one unit of water = trade value of 1.
	taking_angers_god,
	god_angered,
	herbs_reqd;
	requirementsrec r;						// } and ends here.
	Zdrawinfo fix,    // rotation and translation adjustments for 3rd person attack animations
	          dinfo;  // drawing info for the object: type (mesh, model, etc), meshID & texID -or- modelID, scale.
	          								// ... more garbage that doesn't belong here
	int projectile_hit_sfx,  //  used by projectile objects. what wav to play when a projetile hits a target.
											// ... more garbage that doesn't belong here
	    make_sfx,           // what wav to play when the player makes or repairs the object
	    obj_in_hand;        // object to draw in hand during make / find anis
};

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Advertisement


Firstly, if you have shared-per-class data, that's exactly what static class members are for, if the data is constant, all the better but you can't then load it at runtime without subverting the typesystem. If you know it ahead of time and can deal with the trouble of having to edit source to make changes to their values, this is a pretty low-friction solution.

oh that kills! the language can do it but only hard coded! yeah - static constant members get stored with the class def and the vmt and such - just one copy. god its been so LONG since i did this oo stuff. how many years ago did it come out? when it came out it was one of those things i learned then promptly forgot. i was just one guy building games, i wasn't CA architect-ing enterprise software that had to be maintained for 30 years. so it seemed like overkill. i think too many years as a professional engineering student (12 or so?) gave me a knack for learning and forgetting things quickly.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


I'd like to apologise; when I gave you the earlier advice I assumed your data structures would contain the data required for their respective tasks not be.. well... crap.

ROTFL!

yes, its hideous, i know. its the result of three years of code growth with zero consideration for cache friendliness, no optimization, and no refactoring ever.

you can see some of the first attempts at refactoring in the requirementsrec. after a year or two i noticed that all skills, actions, and objects were beginning to require similar sets of requirements (parts, tools, and skills) to learn, do, make, or find something. so that's a first attempt at some composition. it would eventually evolve into a requirements object that each skill, object type, and action would have. newer code uses requirementsrecs and generic routines that work with the requirementsrec for anything (skill action, or object).


I will say however don't make a massive "all the fucking things lol" structure for things which don't require it, comments like 'used by food' in with 'used by missiles' is a big blinking sign which says 'warning; this structure is fucked up' which can be seen from space.

so you would advocate splitting the object types down further into things like food vs missiles? but what about when i want to make food a missile so they can have food fights? <g> just kidding!

but seriously, you'd advocate splitting things up a bit more? do remember this is a single instance list of 300 object type defs (currently 273 in use).

that's actually only the half of it. i'm finding that for many things (object types, actions, skills), that i end up with these variables like boolean action_causes_fatigue.

so then i have a few choices, i can implement a lookup table function using a switch - which i do sometimes, or i can stuff the info into a database - which is the trend for later caveman code, or i can do something else i have yet to think of.

so you only see about half of those variables that are specific to only some object types. the rest are implemented as lookup functions. the database method makes things easier for a later move to data driven if desired.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php


char name[100]; // WHY IS THIS HERE? WHY IS THIS NOT A POINTER? That's 100 BYTES per structure you're forcing a fetch on.

its a unique string in the game of size char[100]. it has to go somewhere. and this is an object type definition, the only time update uses an object type definition struct is to lookup missile initial launch speed, missile collision radius, projectile launcher ammo type (for reloading), and the fixup to draw weapon in hand in 3rd person view.

>> // nice naming. Totally useless. Why isn't isLiquid a boolean?

old school. i use ints for booleans.

>> iswpn,ishandwpn,dmg,wpnskill; // Random. Again, booleans? Or a bitset? Or a flags variable?

boolean, boolean, base damage, predefined skill type

>> // If it's used by the missiles, why is it here?

you have to define the collision radius and initial muzzle velocity of a projectile type somewhere. in the object type definitions database seemed the logical place.

are you perhaps confusing this object type definition struct with an entity struct? its an object type definition, its not an entity.

>> // If it's used by the missiles, why is it here?

for each projectile weapon that uses ammo in a game with more than one type of ammo, you must specify on a per weapon basis what kind(s) of ammo it can use.

again, the object definition for the weapon seemed the logical place.

>> int ammotype,

// used by foods
foodboost,foodmoodboost, // random, no type specified? Using , and newlines is A BAD IDEA.

yes. long lists of vars on different lines means you need to scroll up and down to see what type they are.

>> spoilchance, // dice(10000) // Here for what reason?

spoilrate, // ^

once again, more object type definition information. base chance for objects to spoil (assuming they can), and how often the check is made.

>> // Regarding the comment below... WHAT? TIME IN FRAMES? Don't track time by frames.

// time in frames between findchecks / time in frames to make obj

fixed frame time - so i can. i could just as easily say time in 66ms chunks. same difference. also, by frame i mean turn, which means one update not one render.

so more accurately that comment might read: "number of updates between find checks / number of updates to make object".

but by my definition of a frame, 1 frame = 1 turn = 1 update, so that's what it already says - at least by my definition of a frame.

i technically define a frame as 1 frame = 1 turn = 1 input, update, and render cycle. i don't define a frame as 1 frame = 1 render.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

char name[100]; // WHY IS THIS HERE? WHY IS THIS NOT A POINTER? That's 100 BYTES per structure you're forcing a fetch on.


its a unique string in the game of size char[100]. it has to go somewhere. and this is an object type definition, the only time update uses an object type definition struct is to lookup missile initial launch speed, missile collision radius, projectile launcher ammo type (for reloading), and the fixup to draw weapon in hand in 3rd person view.

The only time this struct is used is to lookup MISSILE LAUNCH SPEED? Furthermore, a unique string of exactly 100 characters? Why? What PURPOSE does it serve? You look stuff up by NAME? This doesn't belong here. You don't need a name, if you do, there are plenty of better ways to deal with this, such as an integer id/index into a string pool.

>> // nice naming. Totally useless. Why isn't isLiquid a boolean?

old school. i use ints for booleans.

So you're wasting 4 bytes (at the minimum) for a 0 or 1 value.

>> iswpn,ishandwpn,dmg,wpnskill; // Random. Again, booleans? Or a bitset? Or a flags variable?

boolean, boolean, base damage, predefined skill type

You mean "int, int, int, int". Again, these are separate concerns, why are they in this monolithic structure? Furthermore, if they're bools (and they look like FLAGS to me), and some of them even appear to be mutually exclusive (can you have a weapon that is also a handweapon? or is it weapon or hand weapon?) If it's both, why isn't this a set of flags? An enumeration?.

>> // If it's used by the missiles, why is it here?

you have to define the collision radius and initial muzzle velocity of a projectile type somewhere. in the object type definitions database seemed the logical place.

Projectile != food != weapon. Separate concerns.

are you perhaps confusing this object type definition struct with an entity struct? its an object type definition, its not an entity.

>> // If it's used by the missiles, why is it here?

for each projectile weapon that uses ammo in a game with more than one type of ammo, you must specify on a per weapon basis what kind(s) of ammo it can use.
again, the object definition for the weapon seemed the logical place.

To who? For me the logical place would be to put the missile stuff with the missile concerns. Not to build a giant monolithic structure that holds everything.

>> int ammotype,
// used by foods
foodboost,foodmoodboost, // random, no type specified? Using , and newlines is A BAD IDEA.

yes. long lists of vars on different lines means you need to scroll up and down to see what type they are.

So you did it why?

>> spoilchance, // dice(10000) // Here for what reason?
spoilrate, // ^

once again, more object type definition information. base chance for objects to spoil (assuming they can), and how often the check is made.

If something can't spoil, you probably shouldn't be providing data on how it can spoil, eh?

>> // Regarding the comment below... WHAT? TIME IN FRAMES? Don't track time by frames.
// time in frames between findchecks / time in frames to make obj

fixed frame time - so i can. i could just as easily say time in 66ms chunks. same difference. also, by frame i mean turn, which means one update not one render.

so more accurately that comment might read: "number of updates between find checks / number of updates to make object".

but by my definition of a frame, 1 frame = 1 turn = 1 update, so that's what it already says - at least by my definition of a frame.

i technically define a frame as 1 frame = 1 turn = 1 input, update, and render cycle. i don't define a frame as 1 frame = 1 render.

Then you're using a different terminology than the rest of the world. Which is another bad idea.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Not sure if this is on track. but in my asset manager I needed a DWORD type for object ids and the edtor neede string type so users could select object types

they want on the map.

did this

class cType

{

DWORD Type;

std::string friendlyType;

//rest of class

};

class TypeManager

{

static std::map<DWORD, cType> alltypes;

//use a init functions to first define all types

static LoadTypes()

{

//define your first object by hand

cType type;

type.Type = 1;//the object id used in game

type.friendlyType = "Orc Killer";//this is use in editors for user ease

//add using a function so you can see if its allready in the map incase you endered invalid data twice user error checking

AddNewDefinedObject(type);

//add more

}

something like that works for me you only ever need 1 and 1 time init . I have no problem with the static class yet

you could do the same by file as well .(this should save me from the static critic, lol sorry)rolleyes.gif


As for where you put the data when you want it in memory... where ever you want.

so there is no best method then?

I would recommend where it is easy to modify and find the data. This allows you to iterate quickly.


oh that kills! the language can do it but only hard coded!

You can have shared class variables that aren't constant -- 'static' is a bit confusing in C++ parlance because its an overloaded term and none of the things it means in C++ are what it means in English. Go figure. I merely meant that if you want them to be constant and define them as "static const int" or whatever, then you have to assign it a value right away -- you can't defer initialization to an arbitrary point that's convenient for you.

However, doing a little more reading myself, you very much can initialize the value from a file at its definition, by assigning it the return value of a function that reads from the file. According to the standard, these static data members (which happen to be const here) are guaranteed to be initialized A) in the order in which they're declared in the translation unit, and B) all such members are initialized before any function or object defined in the same translation unit is used.

Now, this somewhat complicates how you might normally initialize a bunch of variables you're reading from a file because you probably want the static members to be individual entities in the interface, but you only want to open and parse the file once. Those things are at odds here. One work around would be to define a structure to hold all these shared values (but it adds length to the name of each shared value, which could be good or bad, depending on your tastes) and have the function return the whole structure. Another approach which avoids the superfluous struct would be to create a file-reading interface that internally caches the file or parses all its contents on first read (basically the Memoizing pattern) and then just passes out what it already knows when the other bits are asked for. You can signal the system to release the cached info when you first instantiate the object (or one of the objects) associated with the file, because point B) above, from the standard, guarantees you'll be done with the file at that point. The dumb approach, which might be just fine if the files are small and since you're only doing this once, would be to just read the file anew each time and pick out the one value you're interested in -- this might not actually perform that poorly, even since if you initialize them in rapid succession (and why wouldn't you) the HDD will have the file contents cached in its internal RAM on a block-by-block basis, especially if you don't have to parse the structure of the file to understand where the data you want is (though this loses some flexibility of being data-driven).

throw table_exception("(? ???)? ? ???");

well, it looks like i got my answer for entities: just put the type info in each entity, initialized from some sort of descriptor. the cache friendliness outweighs the ram overhead.

but it also looks like folks have different ideas about how object info should be organized. so i guess i'll be starting a new thread to solicit opinions on how it ought to be organized - perhaps after some thinking about it myself first.

as usual, thanks to all for the advise.

Norm Barrows

Rockland Software Productions

"Building PC games since 1989"

rocklandsoftware.net

PLAY CAVEMAN NOW!

http://rocklandsoftware.net/beta.php

This topic is closed to new replies.

Advertisement