Home » Stars! Clones, Extensions, Modding » FreeStars » Design Delimma
Design Delimma |
Fri, 13 June 2003 15:26 |
|
LEit | | Lt. Commander | Messages: 879
Registered: April 2003 Location: CT | |
|
I've got an OO design delimma, if anyone can help, I'd appreciate it.
I'm trying to design a Ship class that is composed of a collection of Components.
Each component can be of a certain type (armor, shield, weapon, and several others). Some components can have capabilities of more then one type (armor with some shielding). So far it's pretty easy, use inheritance for the types, and multiple inheritance for the multiple type components.
The complication is that I'd like to be able to read a file of components:
Croby Sharmor, type shield, (generic details, cost, mass, etc), shield strength 65, armor strength 60.
Most shields don't have armor, this one is special. There are some components that have 5 or more sub capabilities, and I'd even like to leave that open, so defining a class for each possible combination is impractical. I want to read a file and make a list of components from that file.
Two options I've considered are:
Make Component class generic enough to handle every combination (put shield and armor and all other variables into it) and have it return some default value if a particular component isn't defined.
The other idea is have Component have a list of Capabilities, and you add to the list as needed. Then you can ask a Component to give you the Armor Capability. This is what I'm currently leaning toward, I feel it's a better design, however, it'll be slightly harder to code.
[Updated on: Thu, 19 June 2003 11:20]
- LEitReport message to a moderator
|
|
|
Re: Design Delima |
Fri, 13 June 2003 20:47 |
|
|
A third option is to create the required component class on the fly using multiple inheritance to get the various quialities. Of course I'm a java programmer and I have no idea whether you can do that in C++.
Failing that I'd use the second choice...and create an accessor method to access the list contents.
Report message to a moderator
|
|
|
Re: Design Delima |
Mon, 16 June 2003 20:42 |
|
boneandrew | | Crewman 1st Class | Messages: 35
Registered: June 2003 Location: Detroit | |
|
I'm a bit rusty with my coding skills right now, but as I understand it, Java is basically C++ with direct control over pointers removed. And actually, I think dealing with classes/inheritance issues is easier with Java. But yes, any of this stuff is possible with C++.
There are quite a few ways of handling this, actually. Another option would be to define each component as an object that directly adjusts the ship's capabilities. For example, you define the Croby Sharmor as an object that will only fit into shield slots (or shield/elec/mech, whatever), and whenever Stars! accesses the ship, it calls each object and the Croby Sharmor adds the 65 shields and 60 armor per object. (Such objects actually contain executionable code, not just data.) No other condition checking needed. You'd need to define each component, but this will make it VERY easy to design those MT trader parts and other components that have multiple abilities (for example, pen scanners).
Or, if you're really worried about saving memory space, do a condition check on the type of component in each slot and call an appropriate procedure that similarly adds the required armor/shields/whatever. This'll take up less memory space as you're not storing separate exectuionable code in active memory for each component for each ship, but more coding as you'll need to define all the condition checks and stuff. And it'll probably be a little slower.
Anyway, some ideas that I hope help.
[Updated on: Mon, 16 June 2003 20:43] Report message to a moderator
|
|
| |
Re: Design Delima |
Mon, 16 June 2003 21:58 |
|
John Marasco | | Crewman 3rd Class | Messages: 5
Registered: June 2003 | |
|
If it's not too late for suggestions you could define a <component> class that contains an array of <sub component> classes. The <sub component> classes are based on a virtual <basic component> class that provide methods like IsShield, IsArmor, IsComputer, PrimaryValue, SecondaryValue, etc... The <component> class will contain a function to parse the array and return pertinent parameters from the various <sub component> classes. Disk storage could be via XML, which would mirror the in memory structure. The <component> class could even have generic GetArmor, GetShield, GetComputer, GetJamming, GetManeuverJet, etc... methods built into it to parse and find the basic <sub component> variables and then more generic methods for future <sub component> types. You could build all this functionality into the <component> class (like you suggest) which is the equivalent of the multi-inheritance component class. The trouble is that you cannot strip off individual component objects and pass them to other functions unless you actually have "sub components" to strip off.
Report message to a moderator
|
|
| |
Re: Design Delima |
Tue, 17 June 2003 20:16 |
|
John Marasco | | Crewman 3rd Class | Messages: 5
Registered: June 2003 | |
|
Oops, I see now where you had already mentioned this option.
One of the nice things about using an array of Capability classes is that you can then pass the capabilities up to various ship-sub systems to calculate composite values (as opposed to simple values like armor and/or shields).
cloaking is cloaking capability combined with ship mass calculated by the cloaking ship sub system class.
accuracy is computer capability combined with torpedo/missile accuracy calculated by the targeting ship sub system class.
maneuverability is the maneuvering jet capability combined with the engine capability combined with ship mass and can be calculated by the maneuver ship sub system class.
etc...
The ship class is then a container class (hull) that accesses various sub-systems stored in an array and passes component capabilities classes to sub-system classes to calculate ship parameters (like cloaking, targeting, maneuverability, etc...).
You could expand a specific capability class and/or add new capabilities to support new ship sub systems in future enhancements without having to hack into unrelated classes.
As with most OO designs, it's more work up front but provides a component based method for expanding the systems.
You could get away with using as few as 4 bits for any Stars! capability and map that 4 bit number to whatever final range is desired in the interface methods. You could do so through a hardcoded map (00 = 25, 01 = 40, 02 = 60, 03 = 100, 04 = 175, 05 = 300, 06 = 500, 07 = 75, 08 = 120 for shields) or you could calculate a non-linear algorithm to give the desired range and variation therein. It saves space but at the expense of limited flexibility to change values. It is probably a pointless exercise given the number of capabilities stored in RAM during a game (not many) and how cheap a gig of RAM is these days. If you had any thoughts of someday expanding the game to extreme sizes such techniques applied today could pay off later when trying to expand the game to handle 100s (1000s) of players.
If the source for this project is public? I wouldn't mind following along if it's allowed...
These are approaches you may have already considered and rejected. If so then excuse the ramble.
Report message to a moderator
|
|
|
Re: Design Delima |
Wed, 18 June 2003 13:09 |
|
LEit | | Lt. Commander | Messages: 879
Registered: April 2003 Location: CT | |
|
In reverse order:
The source will be public, but I don't yet have it publicly accessable. It's no where near complete, and is changing daily. If you still want it I can send it to you.
Compression is NOT a goal, I'll be using integeres to represent most of the data, maybe a floating point value here or there. Expanding to handle massive numbers is also not a goal. If some one wants to do that, and the code doesn't collapse based on the data size, that's fine, but I'm not going to worry about it.
I've currently made Components be a monolithic class that has every capability stored in it, it's about 50 numbers and 5 lists of PRTs/LRTs/Hulls that are disallowed/allowed (Component can (cannot) be used by this PRT/LRT or on this hull). One of the lists is not stricly needed (I've got both allowed and denied PRTs) but it should make defining and coding them easier.
I don't undertand how passing a Capability instead of a Component makes things any easier (or harder). The Ship class will still have to extract the information from the Capability or Component and do calculations with it. Cloaking, for example, is highly dependant on what other components are on the ship and not just the mass, that calculation seems to go naturally into the Ship class.
- LEitReport message to a moderator
|
|
| | |
Re: Design Delimma |
Fri, 26 March 2004 07:07 |
|
|
If you use a generic component class that contains all potential variables, all that would be needed is to return null for any variable that the component doesn't have. There isn't any reason that you couldn't have an engine with a shield component for example.
A component type needs to be used to define what type of slot the component occupies and the class contains all variables that any component could have - only one class is then needed.
Ptolemy
Though we often ask how and why, we must also do to get the answers to the questions.Report message to a moderator
|
|
| |
Re: Design Delimma |
Fri, 26 March 2004 14:21 |
|
|
Well, personally, I'd just store it in a dynamic array of structures.
Ptolemy
[Updated on: Fri, 26 March 2004 14:22]
Though we often ask how and why, we must also do to get the answers to the questions.Report message to a moderator
|
|
|
Re: Design Delimma |
Fri, 09 April 2004 13:42 |
|
|
In my opinion you want to do it as a relational database (multiple seperate tables). The actual database can be xml or some other simple text format and loads like a spreadsheet into whatever structure your server feels like using.
In real application, Id field would likely be larger than 3 digits to make more room for expansion.
So for example you might have 3 tables:
Table language
Id, Code*, Name
301, SmtB, Smart Bomb
302, CrbS, Crobby Shield
501, Bomb, Bomb
502, Shld, Shield
503, GenP, General Purpose slot
801, AShi, Add Shields
802, AShN, Add Shield with no gain from RS
803, AArm, Add Armour
804, AArN, Add Armour with no loss to RS
805, ASma, Add Smart Bomb
*code field is for making modifications easier in the long term. One day translator programs could convert Id and Code back and forth in other tables if they prefer a code in their language rather than remembering an id. Number ranges would be clearly defined including space for public expansion and end user private addins.
Table SlotType
Id, Slot
301, 501
302, 502
302, 503
Table Property
Id , Property, Value
301, 805, 15
302, 801, 60
302, 804, 65
As an added note: I feel relational design is more flexible than object oriented, it is easier to change your perspective of what is on top... the base object.
For example sometimes you may care about all the properties that are members of a ship component such as crobby shield. Other times you may want to go through all components that are members of the shield property. Using pointers and hash collections to link, you can squeeze out amazing performance as well in a memory based database.
[Updated on: Fri, 09 April 2004 13:54] Report message to a moderator
|
|
|
Re: Design Delimma |
Sat, 10 April 2004 02:00 |
|
|
We don't need the complexity of a relational database - there simply isn't enough stuff to require it. Look at Stars! now in a maxed out testbed and you don't have all that many components. Also, components are grouped based on type (scanners, egines etc.) An indexed array of structures containing the componet variables works fine. A base identifier is assigned to each component family (i.e. 0 = engine and the first entry (0) is the QJ5 engine).
#define Engine 0
now all I need to do is get the array entry for the Fuelmizer (for exapmle) which is engine 3 so I go get array entry 2 for the engine. It's lightning quick and efficient.
The actual initial values for the components can be stored in a separate file initially so that they can be changed and, that file can be loaded at startup - or, they can all be stored in the .ini file. At some point though, they will need to be encoded into the executables as fixed values so that they can't be changed by players.
Ptolemy
[Updated on: Sat, 10 April 2004 02:01]
Though we often ask how and why, we must also do to get the answers to the questions.Report message to a moderator
|
|
| | | |
Re: Design Delimma |
Sat, 10 April 2004 11:15 |
|
|
Ptolemy wrote on Sat, 10 April 2004 00:00 | We don't need the complexity of a relational database
|
You don't need the 'complexity' of a String class. Simple char* will do. You don't need the complexity of C++, simple assembler will do.
The idea with a relational system is you end up with a simpler rather than more complex design, you don't have to manually setup, link structures, load, etc (which otherwise may get done different ways and then have to go back and figure out how was done). And if you like STL, object oriented, etc., you can layer it on top.
Arrays (such as char* compare to String class) have a way of sometimes leading to stray pointers and crashes after little mistakes.
The reason programs like DBase III and later MS Access became popular for simple jobs was not because people wanted to work harder.
Report message to a moderator
|
|
|
Re: Design Dilemma |
Sat, 10 April 2004 12:41 |
|
|
multilis wrote on Sat, 10 April 2004 11:15 |
Ptolemy wrote on Sat, 10 April 2004 00:00 | We don't need the complexity of a relational database
|
You don't need the 'complexity' of a String class. Simple char* will do. You don't need the complexity of C++, simple assembler will do.
The idea with a relational system is you end up with a simpler rather than more complex design, you don't have to manually setup, link structures, load, etc (which otherwise may get done different ways and then have to go back and figure out how was done). And if you like STL, object oriented, etc., you can layer it on top.
Arrays (such as char* compare to String class) have a way of sometimes leading to stray pointers and crashes after little mistakes.
The reason programs like DBase III and later MS Access became popular for simple jobs was not because people wanted to work harder.
|
What relational db product are you suggesting that will work across multiple platforms as a general host?
Or are you suggesting that the initial host be a WINx product only and we'll worry about other operating systems at a later point?
- Kurt
[Updated on: Sat, 10 April 2004 12:42] Report message to a moderator
|
|
|
Re: Design Dilemma |
Sat, 10 April 2004 13:38 |
|
|
Sorry, my answer to Overworked is sort of a ramble, hard to otherwise explain my way of thinking. I present ideas, others may use or discard as they like.
No matter what... NO WINX ONLY! (imo)
Also for all the joys of stuff similar to MS Access, it is a real pain in the butt when using a complex database to have to work around the bugs of others, especially when they add new bugs with each version.
I believe everything for the server should be in non-machine specific c++ to keep life simple. 'Relational' is just a means of organising data, it is possible to do relational with simple collections (groups of objects) with links to other objects.
I normally layer OO code so that everything is idiot resistant from the outside, not possible to have a simple mistake cause a stray pointer or memory leak if following the system. I also now try to keep every reference to an object to one variable, for example printing a name of a component only requires an ID rather than a TYPE and an ID. These ideas are to protect me from my own stupidness.
Code outside an object/system would not do memory allocations with NEW, etc. Instead you work with either global objects or local (on the stack) ones which are really just references to the true objects (and memory management is all automatic and unseen).
...
There are a number of existing open source c++ memory based databases I have been looking at a bit. For my own needs I am mostly trying to build from scratch (simpler, cleaner).
One trick that can be used is to come up with a simple format for database structure that is thrown through a simple c++ translator program that turns it into c++ language objects including both compile time and run time ways to reference fields, eg both t.Id and t("Id") as well as extra objects for linking/indexing/etc.
Loading all the underlying data then becomes a matter of a simple text file describing the structure followed by an extra "#include" and command in your main program to "mydb.load(zPath);" to load the various xml files.
[Updated on: Sat, 10 April 2004 13:39] Report message to a moderator
|
|
|
Re: Design Dilemma |
Sun, 11 April 2004 13:15 |
|
Kotk | | Commander | Messages: 1227
Registered: May 2003 | |
|
I think there are a bit more levels of abstraction needed for proper OO.
"fleet" consists of "tokens"
"token" is design ID, number of ships, damage level, number of damaged ships
"design" consists of "slots". Slot[0] is hull slot.
"slot" is component ID, number of components
"component" is structure of properties
Now some examples:
You want to know if fleet is radiating. Fleet asks from each token if its radiating, token asks if design is radiating, design asks if any slots are radiating and slots check if the component in them is radiating. Component has radiating property.
You want to know the mass of fleet. Fleet asks the mass of tokens and sums them up, token asks the mass of design and multiplies with number, design asks the mass of slots and sums them up, slot asks the mass of component and multiplies with number. Component has mass property.
Report message to a moderator
|
|
| | | |
Goto Forum:
Current Time: Sat May 11 08:22:22 EDT 2024
|