20 Apr, 2010, Kayle wrote in the 61st comment:
Votes: 0
So I read and re-read the things people had said, and I did a little rearranging. So here we go:

class Part
{
private:
Part( const Part & );
Part &operator = ( const Part & );

public:
Part( );
~Part( );

private:
std::string m_Name; // Name of the part

public:
// get functions
const char *getName( ) const { return m_Name.c_str( ); }

// set Functions
void setName( const char *name ) { m_Name = name; }
void setName( const std::string &name ) { m_Name = name; }
};

class Component
{
private:
Component( const Component & );
Component &operator = ( const Component & );

public:
Component( );
~Component( );

private:
/*
* Component Mass is the amount of mass a component takes up on the chassis. A lower mass is better,
* but sometimes lower mass items will be worse than higher mass items.
*/
int m_Mass;
/*
* Component Hit points are the hitpoints a component has before it becomes damaged beyond repair
* and needs replaced. Depending on the component, destruction of the component could result in
* the destruction of the ship.
*/
int m_ComponentHitPoints;

public:
// get functions
int getMass( ) const { return m_Mass; }
int getComponentHP( ) const { return m_ComponentHitPoints; }

// set Functions
void setMass( int mass ) { m_Mass = mass; }
void setComponentHP( int hp ) { m_ComponentHitPoints = hp; }
};

class EnergyDrain
{
private:
EnergyDrain( const EnergyDrain & );
EnergyDrain &operator = ( const EnergyDrain & );

public:
EnergyDrain( );
~EnergyDrain( );

private:
/*
* Reactor drain is the amount of energy a given part pulls away from the reactor to power itself.
* Typically the lower the number the better, as far as power consumption.
*/
int m_ReactorDrain;

public:
// get Function
int getReactorDrain( ) const { return m_ReactorDrain; }

// set Function
void setReactorDrain( int drain ) { m_ReactorDrain = drain; }

};

class Chassis : public Part
{
private:
Chassis( const Chassis & );
Chassis &operator = ( const Chassis & );

public:
Chassis( );
~Chassis( );

private:
/*
* Maneuverability is a combined value that takes a chassis' yaw, pitch and role rates and
* combines them into one simple value, that is used to determine how quickly a ship can
* complete maneuvers of varying types.
*/
short m_Maneuverability; // Base Maneuverabiity rating of the chassis
/*
* Acceleration and Deceleration values are used to determine how quickly a
* chassis can accelerate or decelerate. The value is moved during speeding
* up and slowing down to determine how large each change is.
*/
short m_Acceleration; // Base Acceleration rating of the chassis
short m_Deceleration; // Base Deceleration rating of the chassis
/*
* Weapon mounts are the number of weapons the chassis can support. Your basic starfighter
* will only be able to support between 2 and 4 mounts for blasters. Keep in mind, these
* mounts do NOT count for counter measures or ordinance launchers.
*/
short m_WeaponMounts; // Number of Weapon mounts the Chassis has
/*
* Mass is used to determine how much weight in components a chassis can carry
* Every component will have a mass rating that will be added to the chassis when the
* part is installed.
*/
int m_MaxMass; // Maximum Mass the Chassis can carry
/*
* Hull is the ships hit points rather than individual compnent hitpoints
* Once the current hull reaches 0, the ship is destroyed.
* In the Future, it will be added that destroying a ship produces salvageable
* parts that can be collected based on the Mass of the destroyed ship.
*/
int m_MaxHull; // Maximum Hull Value

public:
// get functions
short getManeuverability( ) const { return m_Maneuverability; }
short getAcceleration( ) const { return m_Acceleration; }
short getDeceleration( ) const { return m_Deceleration; }
short getWeaponMounts( ) const { return m_WeaponMounts; }
int getMaxMass( ) const { return m_MaxMass; }
int getMaxHull( ) const { return m_MaxHull; }

// set Functions
void setManeuverability( short val ) { m_Maneuverability = val; }
void setAcceleration( short accel ) { m_Acceleration = accel; }
void setDeceleration( short decel ) { m_Deceleration = decel; }
void setWeaponMounts( short mounts ) { m_WeaponMounts = mounts; }
void setMaxMass( int mass ) { m_MaxMass = mass; }
void setMaxHull( int hull ) { m_MaxHull = hull; }
};

class Armor : public Part, public Component
{
private:
Armor( const Armor & );
Armor &operator = ( const Armor & );

public:
Armor( );
~Armor( );

private:
/*
* Armor Hit points are something exclusive to the Armor part. Armor hit points absorb damage in
* combat. Once these are exhausted, components themselves begin taking damage.
*/
int m_ArmorHitPoints;

public:
// get Functions
int getArmorHP( ) const { return m_ArmorHitPoints; }

// set Functions
void setArmorHP( int hp ) { m_ArmorHitPoints = hp; }

};

class Booster : public Part, public Component, public EnergyDrain
{
private:
Booster( const Booster & );
Booster &operator = ( const Booster & );

public:
Booster( );
~Booster( );

private:
/*
* Booster Energy is the total amount of energy the booster has until it runs out and needs to
* recharge itself.
*/
int m_BoosterEnergy;
/*
* Consumption Rate is how much of the booster energy is consumed each second the booster is
* engaged.
*/
int m_ConsumptionRate;
/*
* Recharge rate is how much the Booster pulls from the Reactor to recharge itself each second.
*/
int m_RechargeRate;
/*
* Top Booster Speed is the top speed attainable while using the booster.
*/
int m_TopBoosterSpeed;
/*
* The Acceleration rating is how much the booster modifies the ships acceleration rating
* when activated to increase the speed to the booster's top speed.
*/
int m_Acceleration;

public:
// get functions
int getBoosterEnergy( ) const { return m_BoosterEnergy; }
int getConsumptionRate( ) const { return m_ConsumptionRate; }
int getRechargeRate( ) const { return m_RechargeRate; }
int getTopBoosterSpeed( ) const { return m_TopBoosterSpeed; }
int getAcceleration( ) const { return m_Acceleration; }

// set Functions
void setBoosterEnergy( int energy ) { m_BoosterEnergy = energy; }
void setConsumptionRate( int rate ) { m_ConsumptionRate = rate; }
void setRechargeRate( int rate ) { m_RechargeRate = rate; }
void setTopBoosterSpeed( int speed ) { m_TopBoosterSpeed = speed; }
void setAcceleration( int accel ) { m_Acceleration = accel; }

};

class Capacitor : public Part, public Component, public EnergyDrain
{
private:
Capacitor( const Capacitor & );
Capacitor &operator = ( const Capacitor & );

public:
Capacitor( );
~Capacitor( );

private:
/*
* Capacitor Energy is the total amount of energy the capacitor has until it runs out and needs to
* recharge itself.
*/
int m_CapacitorEnergy;
/*
* Recharge rate is how much the Capacitor pulls from the Reactor to recharge itself each second.
*/
int m_RechargeRate;

public:
// get functions
int getCapacitorEnergy( ) const { return m_CapacitorEnergy; }
int getRechargeRate( ) const { return m_RechargeRate; }

// set Functions
void setCapacitorEnergy( int energy ) { m_CapacitorEnergy = energy; }
void setRechargeRate( int rate ) { m_RechargeRate = rate; }

};

class Reactor : public Part, public Component
{
private:
Reactor( const Reactor & );
Reactor &operator = ( const Reactor & );

public:
Reactor( );
~Reactor( );

private:
/*
* Generation rate is the rate at which a reactor generates energy per second.
*/
int m_GenerationRate;
/*
* Charge is how much energy the reactor can hold at any given time. Generally you don't want this
* value to be any higher than needed to give power to all your components.
*/
int m_ReactorCharge;

public:
// get functions
int getGenerationRate( ) const { return m_GenerationRate; }
int getReactorCharge( ) const { return m_ReactorCharge; }

// set Functions
void setGenerationRate( int rate ) { m_GenerationRate = rate; }
void setReactorCharge( int charge ) { m_ReactorCharge = charge; }

};

class Engine : public Part, public Component, public EnergyDrain
{
private:
Engine( const Engine & );
Engine &operator = ( const Engine & );

public:
Engine( );
~Engine( );

private:
/*
* Maneuverability is a combined value that takes an engine's yaw, pitch and role rates and
* combines them into one simple value, that is used to determine how quickly a ship can
* complete maneuvers of varying types. For Engines, Maneuverability is an ammount added to
* the chassis' maneuverability rating to improve it, rather than replace it.
*/
short m_Maneuverability;
/*
* Top Speed is how fast the engine can make the ship move. This is modified by using a booster, or
* replacing it. Mass of a ship will also play a role in how fast it can move. A large ship will not
* recieve the same top speed as a smaller ship using the same engine.
*/
int m_TopSpeed;


public:
// get functions
short getManeuverability( ) const { return m_Maneuverability; }
int getTopSpeed( ) const { return m_TopSpeed; }

// set Functions
void setManeuverability( short val ) { m_Maneuverability = val; }
void setTopSpeed( int speed ) { m_TopSpeed = speed; }

};

class ShieldGenerator : public Part, public Component, public EnergyDrain
{
private:
ShieldGenerator( const ShieldGenerator & );
ShieldGenerator &operator = ( const ShieldGenerator & );

public:
ShieldGenerator( );
~ShieldGenerator( );

private:
/*
* Shield strength is equivalent to the HP of the shields themselves, rather than the shield generator
* which is covered in the components hit points.
*/
int m_ShieldStrength;
/*
* Recharge rate is how much the shields pull from the Reactor to recharge itself each second.
*/
int m_RechargeRate;


public:
// get functions
int getShieldStrength( ) const { return m_ShieldStrength; }
int getRechargeRate( ) const { return m_RechargeRate; }

// set Functions
void setShieldStrength( int str ) { m_ShieldStrength = str; }
void setRechargeRate( int rate ) { m_RechargeRate = rate; }

};

class Weapon : public Part, public Component, public EnergyDrain
{
private:
Weapon( const Weapon & );
Weapon &operator = ( const Weapon & );

public:
Weapon( );
~Weapon( );

private:
/*
* Damage is the base damage the weapon does to a ship. Damage is accompanied by a pair of modifiers
* vsArmor and vsShields. Some weapons are good at damaging armor, but not shields, and these modifiers
* help reflect that.
*/
int m_Damage;
int m_VsArmor;
int m_VsShield;
/*
* Capacitor drain is how much energy per shot a given weapon will drain from the capacitor. If the capacitor
* drain is too high for the capacitor, then the refire rate of the weapon will suffer horribly because it won't
* matter that the weapon can cycle once every 4 seconds when each shot drains the capacitor completely and you
* have to wait till the capacitor recharges to fire again.
*/
int m_CapacitorDrain;
/*
* Refire Rate is the rate at which the weapon cycles and is ready to fire again.
*/
int m_RefireRate;

public:
// get functions
int getDamage( ) const { return m_Damage; }
int getVsArmor( ) const { return m_VsArmor; }
int getVsShield( ) const { return m_VsShield; }
int getCapacitorDrain( ) const { return m_CapacitorDrain; }
int getRefireRate( ) const { return m_RefireRate; }

// set Functions
void setDamage( int dam ) { m_Damage = dam; }
void setVsArmor( int armor ) { m_VsArmor = armor; }
void setVsShield( int shields ) { m_VsShield = shields; }
void setCapacitorDrain( int drain ) { m_CapacitorDrain = drain; }
void setRefireRate( int rate ) { m_RefireRate = rate; }

};

class Ordinance : public Part
{
private:
Ordinance( const Ordinance & );
Ordinance &operator = ( const Ordinance & );

public:
Ordinance( );
~Ordinance( );

private:
/*
* This is the type of ordinance.
* Examples: Proton Torpedo, Concussion Missile, Space bomb, Seismic Torpedo
*/
short m_Type;
/*
* Component Mass is the amount of mass a component takes up on the chassis. A lower mass is better,
* but sometimes lower mass items will be worse than higher mass items.
*/
int m_Mass;
/*
* Lock on Time is the amount of time it takes for the ordinance to acquire a lock on before being
* fired.
*/
int m_LockOnTime;
/*
* Damage is the base damage the ordinance does to a ship. Damage is accompanied by a pair of modifiers
* vsArmor and vsShields. Some ordinance are good at damaging armor, but not shields, and these modifiers
* help reflect that.
*/
int m_VsShield;
int m_VsArmor;
int m_Damage;

public:
// get functions
short getType( ) const { return m_Type; }
int getMass( ) const { return m_Mass; }
int getLockOnTime( ) const { return m_LockOnTime; }
int getVsShield( ) const { return m_VsShield; }
int getVsArmor( ) const { return m_VsArmor; }
int getDamage( ) const { return m_Damage; }

// set Functions
void setType( short type )
{
if( type >= MAX_ORDINANCE || type < 0 )
{
bug( "%s: Invalid ordinance passed: %d", __FUNCTION__, type );
return;
}
else
m_Type = type;
}
void setMass( int mass ) { m_Mass = mass; }
void setLockOnTime( int time ) { m_LockOnTime = time; }
void setVsShield( int val ) { m_VsShield = val; }
void setVsArmor( int val ) { m_VsArmor = val; }
void setDamage( int dam ) { m_Damage = dam; }

};

class Launcher : public Part, public Component
{
private:
Launcher( const Launcher & );
Launcher &operator = ( const Launcher & );

public:
Launcher( );
~Launcher( );

private:
/*
* This is the type of ordinance a given Launcher would deploy.
* Examples: Proton Torpedo, Concussion Missile, Space bomb, Seismic Torpedo
*/
short m_Type;
/*
* Refire Rate is the rate at which the launcher cycles and is ready to fire again.
*/
int m_RefireRate;


public:
short ammoRemaining; // Number of remaining ordinance before the launcher is empty
Ordinance *loadedOrdinance; // Exact type of Ordinance loaded

// get functions
short getType( ) const { return m_Type; }
int getRefireRate( ) const { return m_RefireRate; }

// set Functions
void setType( short type )
{
if( type >= MAX_ORDINANCE || type < 0 )
{
bug( "%s: Invalid ordinance passed: %d", __FUNCTION__, type );
return;
}
else
m_Type = type;
}
void setRefireRate( int rate ) { m_RefireRate = rate; }

};

class CounterMeasure : public Part
{
private:
CounterMeasure( const CounterMeasure & );
CounterMeasure &operator = ( const CounterMeasure & );

public:
CounterMeasure( );
~CounterMeasure( );

private:
/*
* This is the type of counter measure.
* Examples: Chaff, Micro-Chaff, EM Emitters, Sensor Decoys, IFF Confusers
*/
short m_Type;
/*
* Component Mass is the amount of mass a component takes up on the chassis. A lower mass is better,
* but sometimes lower mass items will be worse than higher mass items.
*/
int m_Mass;

public:
// get functions
short getType( ) const { return m_Type; }
int getMass( ) const { return m_Mass; }

// set Functions
void setType( short type )
{
if( type >= MAX_CMTYPE || type < 0 )
{
bug( "%s: Invalid counter measure type passed: %d", __FUNCTION__, type );
return;
}
else
m_Type = type;
}
void setMass( int mass ) { m_Mass = mass; }

};

class CMLauncher : public Part, public Component
{
private:
CMLauncher( const CMLauncher & );
CMLauncher &operator = ( const CMLauncher & );

public:
CMLauncher( );
~CMLauncher( );

private:
/*
* This is the type of counter measure a given CMLauncher would deploy.
* Examples: Chaff, micro-chaff, EM Emitters, Sensor Decoys, IFF Confusers
*/
short m_Type;
/*
* Refire Rate is the rate at which the launcher cycles and is ready to fire again.
*/
int m_RefireRate;

public:
short remainingUses; // Number of remaining uses of the CMLauncher before it's empty
CounterMeasure *loadedCM; //Exact Counter Measure loaded

// get functions
short getType( ) const { return m_Type; }
int getRefireRate( ) const { return m_RefireRate; }

// set Functions
void setType( short type )
{
if( type >= MAX_CMTYPE || type < 0 )
{
bug( "%s: Invalid counter measure type passed: %d", __FUNCTION__, type );
return;
}
else
m_Type = type;
}
void setRefireRate( int rate ) { m_RefireRate = rate; }

};

class Ship
{
private:
Ship( const Ship & );
Ship &operator = ( const Ship & );

public:
Ship( );
~Ship( );

private:
// Registry
std::string m_Name;
std::string m_Filename;
std::string m_Owner;
std::string m_Pilot;
std::string m_CoPilot;

// Parts
Chassis *m_Chassis;
Reactor *m_Reactor;
Engine *m_Engine;
Booster *m_Booster;
Capacitor *m_Capacitor;
Armor *m_ForeArmor;
Armor *m_AftArmor;
ShieldGenerator *m_ShieldGenerator;
Launcher *m_Launcher;
CMLauncher *m_CMLauncher;

// Misc
Room *m_InRoom;

public:
FlagSet< MAX_SHIPFLAG > flags; // Flags for Ships.
Vector *position; // Current Position of the ship.
Vector *heading; // Current Heading of the ship.
bool shipSettings[MAX_STATUS]; // Status of ship, see shipStatuses for all possible.
int currentMass; // Current Mass the Chassis is carrying
int currentHull; // Current Hull Value


// get Functions
const char *getName( ) const { return m_Name.c_str( ); }
const char *getFilename( ) const { return m_Filename.c_str( ); }
const char *getOwner( ) const { return m_Owner.c_str( ); }
const char *getPilot( ) const { return m_Pilot.c_str( ); }
const char *getCoPilot( ) const { return m_CoPilot.c_str( ); }
Chassis *getChassis( ) const { return m_Chassis; }
Reactor *getReactor( ) const { return m_Reactor; }
Engine *getEngine( ) const { return m_Engine; }
Booster *getBooster( ) const { return m_Booster; }
Capacitor *getCapacitor( ) const { return m_Capacitor; }
Armor *getForeArmor( ) const { return m_ForeArmor; }
Armor *getAftArmor( ) const { return m_AftArmor; }
ShieldGenerator *getShieldGenerator( ) const { return m_ShieldGenerator; }
Launcher *getLauncher( ) const { return m_Launcher; }
CMLauncher *getCMLauncher( ) const { return m_CMLauncher; }
Room *getInRoom( ) const { return m_InRoom; }

// set Functions
void setName( const char *name ) { m_Name = name; }
void setName( const std::string &name ) { m_Name = name; }
void setFilename( const char *name ) { m_Filename = name; }
void setFilename( const std::string &name ) { m_Filename = name; }
void setOwner( const char *name ) { m_Owner = name; }
void setOwner( const std::string &name ) { m_Owner = name; }
void setPilot( const char *name ) { m_Pilot = name; }
void setPilot( const std::string &name ) { m_Pilot = name; }
void setCoPilot( const char *name ) { m_CoPilot = name; }
void setCoPilot( const std::string &name ) { m_CoPilot = name; }
void setChassis( Chassis *chassis );
void setReactor( Reactor *reactor );
void setEngine( Engine *engine );
void setBooster( Booster *booster );
void setCapacitor( Capacitor *capacitor );
void setForeArmor( Armor *armor );
void setAftArmor( Armor *armor );
void setShieldGenerator( ShieldGenerator *shieldGen );
void setLauncher( Launcher *launcher );
void setCMLauncher( CMLauncher *cmlauncher );
void setInRoom( Room *room ) { m_InRoom = room; }

// Placing/Removing from rooms while landed.
bool toRoom( Room *room );
bool extractFromRoom( );

// I/O
void save( );
void saveToFile( );
void readFromFile( FILE *fp );

// Vector Stuff
bool isFacing( Ship *target );
bool isFacing( Planet *target );
void turn180( )
{
heading->x *= -1;
heading->y *= -1;
heading->z *= -1;
}
void setCourse( Ship *destination );
void setCourse( Planet *destination );
void setCourse( Starsystem *destination );
// This could be so fun to play with.
void setCourse( Star *destination ); // This could result in a lot of "Oops…" moments.
void alignHeading( Ship *target ) { heading->copy( target->heading ); }
};


Any further input is more than welcome, and if you want to pull sections off and modify them to illustrate what you're talking about feel free, I learn best from examples, because I don't always understand plain words. ;)

P.S. I still hate the coloring for the syntax highlighting… The it's just really bad. :P pastebin.com is a good example of readable syntax highlighting that doesn't suck. :P
20 Apr, 2010, Runter wrote in the 62nd comment:
Votes: 0
Well…you coulda pastebin'd it and linked it. :p

700 lines like that kinda makes me eyes glaze over anywho.
20 Apr, 2010, shasarak wrote in the 63rd comment:
Votes: 0
@Kayle:
Some of your class names don't do a very good job of describing what each class does. :smile: It's not at all obvious from the naming what the distinction is between a Part and a Component.

I suspect that Component should actually be a subclass of Part; components should inherit from that rather than from both Part and Component independently. (Is it possible for something to inherit from Component but not from Part?)

I'm not 100% convinced that Armor hitpoints are really entirely distinct from Component hitpoints, although they could be; it may also be that the way shields work could also use component hit points.

I'm not sufficiently familiar with C++ programming that I fully understand what the things under the class declaration which look like private constructor methods are supposed to do - could you clarify that?

Are Booster, Capacitor and ShieldGenerator recharge rates really a different thing from EnergyDrain?

There are a few potential issues with Physics. I'm not that familiar with the physical laws of the Star Wars universe, and I don't know how you want to model them; but in the real world acceleration and deceleration would not be stored properties of the chassis. To work out the ship's acceleration you would calculate the thrust of the engines (allowing for boosters) and then divide that by the (dynamically calculated) mass of the ship (including chassis, components, cargo and crew). In terms of manouverability, that probably would include some properties of the chassis - the overall shape of the hull (distribution of mass relative to x, y and z axes) and the position and power of manouvering thrusters would determine how quickly the ship can rotate; once angled correctly, the acceleration and deceleration properties already discussed would determine how quickly you can change heading to the desired value. You may well not want to use Newtonian physics, but you should definitely sit down and think about how you do want physics to work. If you put a more powerful engine on the same chassis, should the ship then be able to accelerate faster?

Perhaps things like Ordinance should also inherit from Component - a lucky hit might damage a missile inside the hull.

What you're calling Weapon and Ordinance should probably have some sort of common superclass which deals with armor damage and shield damage. And Weapon and Launcher maybe should both inherit from something which handles refire rate.

You might also want to abstract the process of energy drain and energy transfer a bit more. It seems like you have a concept of something which produces energy, something which stores energy, and something which drains energy. A reactor generates energy, but can store it for only a single tick (perhaps). A generic ship's component might drain energy. Something like a capacitor drains energy from something else (the reactor) in order to store it within itself; and can in turn be drained by something else. So you should probably start with classes like "EnergyProducer", "Energy Storer", and "Energy Drainer" (the last of already got) and then have classes like Capacitor and Shield Generator inherit from them.

An important aspect of class design is to get the level of abstraction right. Don't just think in terms of "a Weapon drains energy from a Capacitor", also think in terms of "things drain energy from other things" (without worrying about what those things are), implement that mechanism, then make a Weapon being fed by a Capacitor just one specific example of that sort of thing happening.
20 Apr, 2010, David Haley wrote in the 64th comment:
Votes: 0
Quote
I'm not sufficiently familiar with C++ programming that I fully understand what the things under the class declaration which look like private constructor methods are supposed to do - could you clarify that?

Basically, they prevent assignment and copying of the object.
20 Apr, 2010, Kline wrote in the 65th comment:
Votes: 0
Kayle said:
So I read and re-read the things people had said, and I did a little rearranging. So here we go:
<big snip>


Pastebin. It's your friend, and ours :)
20 Apr, 2010, Kayle wrote in the 66th comment:
Votes: 0
@Shasarak: I'll have to try and clarify your questions after I've had some sleep. I've found myself entirely unable to sleep at all yet, so I'm very near the 36 hour mark. =/ (Only 4 hours to go…) Hopefully I'll remember to check this again.

Kline said:
Kayle said:
So I read and re-read the things people had said, and I did a little rearranging. So here we go:
<big snip>


Pastebin. It's your friend, and ours :)


Nope, I don't like the MudBytes pastebin's syntax highlighting either. :P
21 Apr, 2010, quixadhal wrote in the 67th comment:
Votes: 0
It would be easy in Visual Studio (because it knows the syntax tree)… it would be a nightmare in vim.
21 Apr, 2010, Kaz wrote in the 68th comment:
Votes: 0
shasarak said:
I'm not sufficiently familiar with C++ programming that I fully understand what the things under the class declaration which look like private constructor methods are supposed to do - could you clarify that?


A declared (but not defined) copy constructor and assignment operator is the "noncopyable idiom". It ensures that an object cannot be copied. It's pretty normal for objects in a heirarchy that are not simple containers of values. Any attempt to copy the object would be incorrect, and flagged up as a linker error rather than a particularly hideous runtime error.

(Edit: Oh, David got there first. Apologies for the noise.)
21 Apr, 2010, Kayle wrote in the 69th comment:
Votes: 0
shasarak said:
I suspect that Component should actually be a subclass of Part; components should inherit from that rather than from both Part and Component independently. (Is it possible for something to inherit from Component but not from Part?)

This makes sense, becuase it's not possible to inherit from component and not part. Everything has a name, but not everything has the hitpoints, etc of Component. Maybe Part needs a better name. I just didn't want to call it "Name" since that's all it deals with.

shasarak said:
I'm not 100% convinced that Armor hitpoints are really entirely distinct from Component hitpoints, although they could be; it may also be that the way shields work could also use component hit points.

Hadn't really thought about this much. Armor Hitpoints won't ever go down on the actual armor itself. It's actually just storage for a number transferred to the Ship itself. IE. Gallofree Starship Armor has 800 points of Armor HP, So the Ships forearmor is 800 points. On the ship that 800 will go down and when it's gone, then components inside will take damage. Shields are a whole other beast. The Generator itself has component hitpoints. But the shields like armor are just a value on the ship. If the shield generator takes damage, then the shields are weaker as they get recharged.

shasarak said:
Are Booster, Capacitor and ShieldGenerator recharge rates really a different thing from EnergyDrain?

Yes. Energy drain is a lump sum draw from the Ship's Reactor. The Recharge rates for Booster, Capacitor, and Shield Generators are more about how much of the energy those pieces have taken from the Reactor get converted to usable energy by the things linked to them (engines, weapons, shields respectively) at a time. So if a Capacitor has an Energy drain of 300, and there are 4 weapons hooked to it that drain 20 energy per shot from the Capacitor, for continuous fire the Capacitor would need a recharge rate of about 60/second. That is assuming the weapons were good enough to cycle and be ready to fire once per second.

shasarak said:
There are a few potential issues with Physics. I'm not that familiar with the physical laws of the Star Wars universe, and I don't know how you want to model them; but in the real world acceleration and deceleration would not be stored properties of the chassis. To work out the ship's acceleration you would calculate the thrust of the engines (allowing for boosters) and then divide that by the (dynamically calculated) mass of the ship (including chassis, components, cargo and crew). In terms of manouverability, that probably would include some properties of the chassis - the overall shape of the hull (distribution of mass relative to x, y and z axes) and the position and power of manouvering thrusters would determine how quickly the ship can rotate; once angled correctly, the acceleration and deceleration properties already discussed would determine how quickly you can change heading to the desired value. You may well not want to use Newtonian physics, but you should definitely sit down and think about how you do want physics to work. If you put a more powerful engine on the same chassis, should the ship then be able to accelerate faster?

I'm not really a physics guy. But I'm not trying to completely over complicate the system. Which is why I went with the simplicity of just assigning values to maneuverability, acceleration and deceleration values on the chassis. I want the system to be realistic, but at the same time fun, and easy for people to understand and work with. Although I do have a former NASA Astrophysicist, and more recently a Physics major on staff at SW:TSW. Maybe there's a balance to be found between real physics and simplicity.

shasarak said:
Perhaps things like Ordinance should also inherit from Component - a lucky hit might damage a missile inside the hull.

I'm all for realism, but this might be a step too far. But I won't rule it out as of yet. A lot of the combat oriented bits of the space system are still very much up in the air, given that I haven't completely worked out ships and all.

shasarak said:
What you're calling Weapon and Ordinance should probably have some sort of common superclass which deals with armor damage and shield damage. And Weapon and Launcher maybe should both inherit from something which handles refire rate.

I had thought about this but ended up being unsure. But since someone else suggested it, I might as well do it. I figured I was being over zealous in my refactoring. But I guess not.

shasarak said:
You might also want to abstract the process of energy drain and energy transfer a bit more. It seems like you have a concept of something which produces energy, something which stores energy, and something which drains energy. A reactor generates energy, but can store it for only a single tick (perhaps). A generic ship's component might drain energy. Something like a capacitor drains energy from something else (the reactor) in order to store it within itself; and can in turn be drained by something else. So you should probably start with classes like "EnergyProducer", "Energy Storer", and "Energy Drainer" (the last of already got) and then have classes like Capacitor and Shield Generator inherit from them.

I don't know how well this would work. Because every piece takes energy from the Reactor, weapons require energy from the reactor itself to function, but the energy for the actual shots comes from the Capacitor. The Reactor provides power to the whole ship, things like Boosters, and Capacitors, drain a lump of energy to convert to a lesser or more concentrated type of energy, for the extra use of other parts.

shasarak said:
An important aspect of class design is to get the level of abstraction right. Don't just think in terms of "a Weapon drains energy from a Capacitor", also think in terms of "things drain energy from other things" (without worrying about what those things are), implement that mechanism, then make a Weapon being fed by a Capacitor just one specific example of that sort of thing happening.

And this is where a lot of my issues with coming from a procedural background come in. I have issues changing my entire thought process so that I can think of things in the OO paradigm. But I can't just completely switch over to an OO paradigm because the majority of the codebase is still very much procedural.
21 Apr, 2010, shasarak wrote in the 70th comment:
Votes: 0
Kayle said:
shasarak said:
I suspect that Component should actually be a subclass of Part; components should inherit from that rather than from both Part and Component independently. (Is it possible for something to inherit from Component but not from Part?)

This makes sense, becuase it's not possible to inherit from component and not part. Everything has a name, but not everything has the hitpoints, etc of Component. Maybe Part needs a better name. I just didn't want to call it "Name" since that's all it deals with.

NamedShipPart, maybe? :thinking:


Kayle said:
shasarak said:
I'm not 100% convinced that Armor hitpoints are really entirely distinct from Component hitpoints, although they could be; it may also be that the way shields work could also use component hit points.

Hadn't really thought about this much. Armor Hitpoints won't ever go down on the actual armor itself. It's actually just storage for a number transferred to the Ship itself. IE. Gallofree Starship Armor has 800 points of Armor HP, So the Ships forearmor is 800 points. On the ship that 800 will go down and when it's gone, then components inside will take damage. Shields are a whole other beast. The Generator itself has component hitpoints. But the shields like armor are just a value on the ship. If the shield generator takes damage, then the shields are weaker as they get recharged.

Well… yes… but that's not really the point I was making.

Again, think of "hit points" in a more abstract way. In the abstract, "hit points" are simply a number which is reduced when the thing that has hit points takes damage. When the hit points are reduced to zero, the thing in question is destroyed. Now, is that description valid for, say, a shield? Is it the case that the shield has a certain number of hit points, that the number is reduced when the shield is hit by weaponfire, and that when the hit points reach zero, the shield collapses?

Let's suppose for the sake of argument that this is correct. Now compare that to Armor (and for the moment assume that the Armor manages its own hit points rather than having the chassis do it, which is probably the better way to do it). Again, the armor has a certain number of hit points, it takes damage from weapon fire, when points reach zero, the armor is destroyed.

Compare with a ship's component: it has a certain number of hit points, taking direct fire reduces the point total, when the points reach zero the component is destroyed.

Reading over these three descriptions it seems pretty clear that all three are essentially the same mechanism. That suggests that components, armor and shields should probably all inherit from some kind of ThingThatHasHitPoints class. Once you start to think about behaviour instead of merely properties, you might (for example) have a method which is defined on that superclass and which is invoked when the hitpoints hit zero. That might perhaps be a pure virtual function on the superclass, which is overridden by each of the subclasses we're talking about. So, the superclass models the aspects of behaviour that are common to all things of that type - it can take hitpoint damage, and when the points hit zero something bad happens - while the subclasses control the aspects of behaviour that are unique to them - i.e. exactly what bad thing happens when the points reach zero.

Now, you're right that something somewhere along the line has got to handle what it is that takes damage when the ship is hit by incoming fire. Off the top of my head, it might make sense for the chassis to manage that process. Incoming damage would be passed to the shield which would reduce the damage by an amount that would vary depending on its current strength; the remainder might be passed to the armor; any that is not absorbed by the armor would affect components, etc. So, yes, the process of the ship as a whole taking damage is a calculation which ultimately involves more than one set of hitpoints used in the calculation in different ways; but there might well still be enough common behaviour between those different kinds of hitpoints that they could be stored in the same way, or inherited from the same superclass.

Kayle said:
And this is where a lot of my issues with coming from a procedural background come in. I have issues changing my entire thought process so that I can think of things in the OO paradigm. But I can't just completely switch over to an OO paradigm because the majority of the codebase is still very much procedural.

(nods)

Arguably I'm over-analysing this a bit; if you've got two different classes which do the same thing, it's hardly the end of the world if there's a little duplication of code between them, and you don't have to rabidly push everything up to a common superclass every time (indeed there are times when you actively shouldn't); but, as well as actually coming up with a practical system, I'm hoping we can use this as an example to illustrate the sort of thought processes that go into OO design. I realise that you're not familiar with them, which is why I'm trying to describe them!

More later, possibly. :smile:
21 Apr, 2010, 3squire wrote in the 71st comment:
Votes: 0
See, what I always find complicated in data-driven environments is when you want actual language-level inheritance and when you want something considerably different, which is sort of a "constructed" inheritance, where the actual game entities inherit from each other based on the data-driven aspects of their functionality rendered from development in the game framework.

A good example: Energy drain. Energy drain could be done through this complicated inheritance hierarchy, but wouldn't it be more appropriate to add it as a "feature" using some sort of Affects system? In which case you could still develop energy drain variants and the whole functionality and yadda yadda and even inherit it down – but instead of inheriting in the functions written into classes, you would write your mud so that the affect is inherited down through object instances rendered in the game. I.e. I design a basic version of a spaceship and specialize it, and the specialized version uses/has energy drain and all even more specialized versions of that simply inherit the attached affect.

This is the solution to the problem presented in this thread in my opinion. You are hard coding your soft code. And that's a mistake.
21 Apr, 2010, David Haley wrote in the 72nd comment:
Votes: 0
What you're describing is essentially implementing a type system separate from the host language type system. Component (as a software design term) systems are good examples of this: you have code objects and classes, but these are quite separate from the in-game objects; you can have game object "class hierarchies" that do not mirror your code's class hierarchy.

I had a post somewhere discussing this notion of type systems within type systems, but don't have it on-hand…

I don't agree that it's necessarily bad to have a class hierarchy that seeks to implement the constructed component hierarchy. There are advantages to either approach. If you want the flexibility, then yes you probably don't want to hard-code the class hierarchy. But if you don't need it, it's a considerable heap of unneeded complexity.
21 Apr, 2010, Runter wrote in the 73rd comment:
Votes: 0
Quote
You are hard coding your soft code. And that's a mistake.


That's not always a mistake. :p

Perhaps you mean in this instance? I'm not sure if it is a mistake here or not. I'll leave that up to Kayle to figure out the needs he has, but I'll leave this behind: Don't build complexity unless you can really justify it today.
22 Apr, 2010, Kayle wrote in the 74th comment:
Votes: 0
3squire said:
A good example: Energy drain. Energy drain could be done through this complicated inheritance hierarchy, but wouldn't it be more appropriate to add it as a "feature" using some sort of Affects system? In which case you could still develop energy drain variants and the whole functionality and yadda yadda and even inherit it down – but instead of inheriting in the functions written into classes, you would write your mud so that the affect is inherited down through object instances rendered in the game. I.e. I design a basic version of a spaceship and specialize it, and the specialized version uses/has energy drain and all even more specialized versions of that simply inherit the attached affect.

This is the solution to the problem presented in this thread in my opinion. You are hard coding your soft code. And that's a mistake.


Changing it into some kind of dynamically assigned effect wouldn't be any less complicated then what I'm doing. I'd have to design the system for inheriting the effects and add all the support. Or I could leave it an Integer and just do some simple math…
23 Apr, 2010, shasarak wrote in the 75th comment:
Votes: 0
3squire said:
See, what I always find complicated in data-driven environments is when you want actual language-level inheritance and when you want something considerably different, which is sort of a "constructed" inheritance, where the actual game entities inherit from each other based on the data-driven aspects of their functionality rendered from development in the game framework.

A good example: Energy drain. Energy drain could be done through this complicated inheritance hierarchy, but wouldn't it be more appropriate to add it as a "feature" using some sort of Affects system? In which case you could still develop energy drain variants and the whole functionality and yadda yadda and even inherit it down – but instead of inheriting in the functions written into classes, you would write your mud so that the affect is inherited down through object instances rendered in the game. I.e. I design a basic version of a spaceship and specialize it, and the specialized version uses/has energy drain and all even more specialized versions of that simply inherit the attached affect.

This is the solution to the problem presented in this thread in my opinion. You are hard coding your soft code. And that's a mistake.

I'm not altogether sure I follow. Are you basically talking about the difference between inheritance and composition?

It's true that use of static, class-based inheritance can be restrictive. I don't know if you caught this thread where I outlined a very fluid and dynamic effects system (starting somewhere around post #55) - is that the sort of thing you're talking about? As the proponent of such a system I am certainly aware of this merits, but suggesting to Kayle that he try and implement something like that in C++ when he's only just started to learn OO seems a bit like suggesting that someone with no previous climbing experience should try and climb K2 in the middle of Winter without any oxygen equipment….
23 Apr, 2010, Kayle wrote in the 76th comment:
Votes: 0
shasarak said:
As the proponent of such a system I am certainly aware of this merits, but suggesting to Kayle that he try and implement something like that in C++ when he's only just started to learn OO seems a bit like suggesting that someone with no previous climbing experience should try and climb K2 in the middle of Winter without any oxygen equipment….


Dude I would so try and climb K2 in the winter with no oxygen equipment… (granted, I'd probably use Conitec GameStudio 6 to build a replica and do it, but still!)
23 Apr, 2010, David Haley wrote in the 77th comment:
Votes: 0
shasarak said:
I'm not altogether sure I follow. Are you basically talking about the difference between inheritance and composition?

It can also come down to the difference between where your types live. You can have inheritance and composition of code classes, or a very simple code class hierarchy that is used to create a very rich data hierarchy. The term "composition" is a little overloaded here and can be used to describe this as well.
23 Apr, 2010, Runter wrote in the 78th comment:
Votes: 0
David Haley said:
shasarak said:
I'm not altogether sure I follow. Are you basically talking about the difference between inheritance and composition?

It can also come down to the difference between where your types live. You can have inheritance and composition of code classes, or a very simple code class hierarchy that is used to create a very rich data hierarchy. The term "composition" is a little overloaded here and can be used to describe this as well.
\

You sure you aren't talking about programming? I think you're talking about programming here. :)
24 Apr, 2010, David Haley wrote in the 79th comment:
Votes: 0
Err, what? Yes, of course I'm talking about programming… why wouldn't I be?
24 Apr, 2010, Runter wrote in the 80th comment:
Votes: 0
David Haley said:
Err, what? Yes, of course I'm talking about programming… why wouldn't I be?


Just letting you know what you're talking about.
60.0/80