08 Mar, 2010, Tyche wrote in the 21st comment:
Votes: 0
Don't worry about getting it right the first time. Refactor. refactor.
My first thought was too many members… possible Stovepipe class.

One possible refactoring is that some of the members might belong
in a ship's registry class, or possibly just Registry as it might apply to
other things beyond ships, like vehicles, cargos, parts, etc.

So….
class Registry
{
const char *m_Name; //Name of the ship.
const char *m_Manufacturer; //Who manufactures it?
const char *m_Shipyard; //Where was it built?
const char *m_Owner; //Who actually owns the ship?
int m_Cost; //Cost of ship < Billion.
int m_BillCost; //Cost of ship > Billion.
};

class Ship
{
Room *m_FirstRoom; //First Room the ship takes up.
Room *m_LastRoom; //Last Room the ship takes up.
Room *m_InRoom; //Room Ship is located in.
Registry *m_Registry; //Registry of the ship.
const char *m_Filename; //Filename of the ship.
const char *m_Pilot; //Who's the Pilot?
const char *m_CoPilot; //Who's the Co-Pilot?
short m_ShipType; //Mob, Player, Custom.
short m_ShipClass; //Class of ship.
short m_HyperDriveClass; //Hyperdrive class.
short m_ShieldType; //Type of Shields
short m_MaxSpeed; //Maximum Sub-light speed.
short m_CurrSpeed; //Current Speed.
short m_TargetSpeed; //Speed ships trying to reach.
short m_Maneuverability; //How Manueverable is the ship?
short m_ShipState; //What's the ship doing?
int m_MaxEnergy; //Maximum Energy Levels
int m_MaxShields; //Maximum Shield Strength
int m_MaxHull; //Maximum Hull Strength
};



Just a tiny refactoring.
08 Mar, 2010, David Haley wrote in the 22nd comment:
Votes: 0
Scandum said:
Sounds kind of messy to end up with 50-100 odd MakeSomething functions, even if Ship::MakeStealthBomber() calls Ship::MakeBomber() which calls Ship::MakeShip() so you inherit the data properly. I guess that's why you'd want to use proper sub classes to begin with, so the entire thing doesn't get top heavy.

Using your metric that more MakeSomething functions are bad, if we apply your metric then not only do we have 50-100 odd Something classes, but each one must have its own constructor and so on and so forth. In other words, if your metric is lines of code required, your solution is at best equally verbose, and at worse more verbose. (That said, using lines of code as a metric of code quality is, as we've discussed many times, rather silly. Don't let Elanthis see this thread.)

Tyche said:
Don't worry about getting it right the first time. Refactor. refactor.

Yes yes. Refactoring is good, and besides the chances are very high that you won't get it right the first time anyhow (few people do, even the very good ones). Not refactoring is a far worse enemy than doing things a few times.
08 Mar, 2010, shasarak wrote in the 23rd comment:
Votes: 0
My initial reaction, like Tyche's, is that your ship class is trying to do far too much… which brings up an important point. When designing classes, it can be very dangerous to think in terms of what a class is; instead, think in terms of what it does. A class should then have the code and data available to it to enable it to fulfill its functions, but no more than that.

So, for example, under what circumstances does the object that represents a ship need to know who manufactured it? What aspect of the behaviour that the ship class models would be different, depending on who the manufacturer is? My guess is that in fact the ship doesn't need to know this - in which case, it shouldn't know it. Giving an object access to more information than it needs is poor encapsulation. (Tyche's suggestion of a ship registry is a good one).

The second (related) point is that many of the properties of ship should probably not be integers, but instead should be objects. If the ship's "engine type" is represented by a number, then one of two things has to happen: 1) the ship has to say to itself "if I have a type 2 engine, then do X, otherwise do Y"; or 2) something outside the ship says "if the ship has a type 2 engine, do X, otherwise do Y". Both of these possibilities, again, represent poor encapsulation. The better way to do this is usually for the ship's engine to be an object in its own right. You then give the enginethe responsibility of deciding whether to do X or Y; and this is accomplished by means of polymorphism.

So, suppose a type 2 engine makes a whirring noise, and a type 3 engine makes a whooshing noise. The wrong approach would be for the ship's code to do this:

if (engineType == 2) {whirr();} else {whoosh();}


The correct way to do it would be to have the ship do:

engine.makeStartupNoise();


and then have different classes of engine implement makeStartupNoise() in different ways (but with each class inheriting from a superclass that defines makeStartupNoise() as a virtual function, and the ship's engine property declared as being of that type).

One indicator of quality OO code, IMO, is that you should virtually neveruse a switch statement - use polymorphism instead.
05 Apr, 2010, JohnnyStarr wrote in the 24th comment:
Votes: 0
Runter said:
I should also mention that where I went to school it was frowned upon to use inheritance for the purpose of code re-use. Although, I think that is one of the obvious benefits that new programmers see when they learn about inheritance. This, in my opinion, can eclipse the real useful applications of it.

This brought back some memories from years past of debates over Inheritance vs Composition over Inheritance. I don't have a firm stance on what way you should take it. I just know how I find myself coding more often these days. :p

Also, keep in mind there's a clear difference between the "is-a" and "has-a" concepts that are common around these types of discussions. To elaborate on what I mean let's take some of my previous example:

class Weapons ; end
class Communications ; end
class Hyperdrive ; end
class Cockpit ; end

class Space_Object ; end # Anything in space?

class Ship < Space_Object
include Cockpit
end

class Fighter < Ship
include Weapons
end

class Satellite < Space_Object
include Communications
end


Everything in space is a Space_Object. A Fighter is a Ship which is a Space_Object. Etc. Do we need to take it this far? Probably not. But this is a clear way to see the difference between "is a" and "has a" relationships.

This may starting to get too complex for our problem, but space can be a complex problem. I'll leave it to you to decide what your game really needs modeled and how much complexity it really needs. A friend of mine says this often, and I often tell him it applies to careers other than his own (architecture) so I'll repeat the quote here: A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away.

That doesn't mean composite objects are bound to this "has a" relationship or true inheritance is exclusive to "is a" relationships. Indeed, here is an example:

class Hominid 
include Head
include Torso
include Legs
include Arms
end
class Person < Hominid
include Student
include Biker
include Programmer
include PokerPlayer
end


My apologies for the tl;dr'r.

Also, one more thing. I would suggest reading about some object oriented patterns. It's a great thing to learn from IMO. A few that come to mind… Composite, adapter, flyweight, proxy, observer, iterator, visitor, singleton, prototype. There's many more. Learning patterns will help you greatly at the end of the day.

I'll be happy to help you with any questions you may have. I'm sure there are others here who will give you their insights on the subject as well.


Very cool, i know this is kind of out of nowhere, but is this something that only Ruby 1.9 allows? I thought using include was limited to Modules, and not Classes?
05 Apr, 2010, Runter wrote in the 25th comment:
Votes: 0
No, you're correct. I should have been including modules, not classes. I didn't run any of it through an interpreter before I posted, unfortunately. :)
06 Apr, 2010, JohnnyStarr wrote in the 26th comment:
Votes: 0
Whats funny is that Ruby claims not to support multiple inheritance, but with Modules and a little metaprogramming (such as class_eval),
you can do it. Not that you should. :devil:
06 Apr, 2010, Runter wrote in the 27th comment:
Votes: 0
JohnnyStarr said:
Whats funny is that Ruby claims not to support multiple inheritance, but with Modules and a little metaprogramming (such as class_eval),
you can do it. Not that you should. :devil:


Well, when people ask if Ruby support multiple inheritance the answer is usually a slow meandering no. A lot of people consider it a feature to not support the type of multiple inheritance that the answer indicates.
08 Apr, 2010, Sorressean wrote in the 28th comment:
Votes: 0
Hello,
I haven't made it through most of the posts, so this is just my observations from the first few.

First, I have a couple of ideas for you. OO is really nice in some cases, but sometimes it tends to clutter things up, so here's my two cents.

If ships are going to be the same in terms of what they do, with different variables, why not store a ship with name, and the variables like max_speed and etc, then when you reload each player's ship/create a ship, you could use a template of sorts to define a fighter. What you could also do is this: (I'm just throwing an example up, it will be different from yours).

class Ship
{
int _pos_x,_pos_y,_pos_z;
int _max_speed;
int _shield_type;
public:
Ship()
{
_shield_type=SHIELD_MINIMAL;
_max_speed=30;
}
};

class Fighter
{
public:
Fighter(void)
{
Ship::Ship();
_max_speed=20;
}
};


Then all your basic functionality would be in the ship's class, and all the constructor would do is initialize to the base variables and set it's own limits. You could also have it add weapons to weapon slots, set rooms, etc. I'm not positive, but I believe it is possible to call the base constructor from an inherited class, if not it's just as easy to do Base::Init();.

Not only does this allow more flexability, but you inherit the getters and setters from the base class,, so your ship derivatives will be relatively small, as opposed to the core class. This also insures that you don't have to re-write lots of code if you add a new variable, unless your fighter needs to change the base variable, at which point you can just change it in the ctor.

Hope that made some sense, anyway.
09 Apr, 2010, Sharmair wrote in the 29th comment:
Votes: 0
Sorressean said:
First, I have a couple of ideas for you. OO is really nice in some cases, but sometimes it tends to clutter things up

At the risk of (re)starting a holy war, I think this is about the first time I have seen a comment like this that dares
to question OOP as being the 'one true way always' so directly. It has been danced around a bit in other treads
though not directly addressed. Games (like MUDs) are really quite different then a lot of other types of programs
and I wonder if pure OOP is really the best solution to code that is so interrelated as MUDs tend to be. Don't get
me wrong here, I see a lot of benefit in using OOP in a lot of places in a MUD, but I question 'pure' OOP (what ever
that really is).
Sorressean said:
class Ship
{
int _pos_x,_pos_y,_pos_z;
int _max_speed;
int _shield_type;
public:
Ship()
{
_shield_type=SHIELD_MINIMAL;
_max_speed=30;
}
};

class Fighter
{
public:
Fighter(void)
{
Ship::Ship();
_max_speed=20;
}
};

There are a few things wrong with this. I assume you really intended class Fighter to be derived from class Ship,
but in either case the code would not work. For class Fighter to be able to change any of the class Ship data,
class Ship would have to declare class Fighter as a friend or in the case of a derived class, have the data
protected (or public). The attempt to call Ship::Ship() is just wrong on so many levels too.
Sorressean said:
I'm not positive, but I believe it is possible to call the base constructor from an inherited class.

Constructors are called automatically for base and member classes before the current class constructor (this
same thing happens in reverse for the destructor too). Other member functions can be called from a derived
class like you tried with your code.
09 Apr, 2010, Runter wrote in the 30th comment:
Votes: 0
Quote
Games (like MUDs) are really quite different then a lot of other types of programs
and I wonder if pure OOP is really the best solution to code that is so interrelated as MUDs tend to be.


Does this type of application lend itself well to object oriented programming less than others? No. If anything it's a better platform to build OOP into.
Can you debate the merits of when and where OOP is used? Sure.
Who's buried in Grant's Tomb? Eh. Who knows? ;)
09 Apr, 2010, Sorressean wrote in the 31st comment:
Votes: 0
I understand that you would have to use getters and setters, I didn't bother, in order to try to make things a bit cleaner looking, (An d yes, I got lazy). With that said, I wasn't aware base ctors got called, so that simplifies my idea down even more than origenally.
09 Apr, 2010, shasarak wrote in the 32nd comment:
Votes: 0
Sharmair said:
At the risk of (re)starting a holy war, I think this is about the first time I have seen a comment like this that dares
to question OOP as being the 'one true way always' so directly. It has been danced around a bit in other treads
though not directly addressed. Games (like MUDs) are really quite different then a lot of other types of programs
and I wonder if pure OOP is really the best solution to code that is so interrelated as MUDs tend to be. Don't get
me wrong here, I see a lot of benefit in using OOP in a lot of places in a MUD, but I question 'pure' OOP (what ever
that really is).

Well, as I am something of an OO jihadi, I should perhaps begin by asking: if you don't know what "pure OOP" is, then are you sure you're qualified to question it? :smile:

It's a given that not every technique available in OO is necessarily applicable in all possible situations. Spanners are very good for tightening nuts; they're not so good for banging in nails. However, this is not a good reason to suggest that toolboxes should not contain spanners or that, if you do actually have some nuts to tighten, you should explore non-Spanner-Oriented approaches first.

It is certainly possible to make a mess of a MUD by using OO concepts and approaches inappropriately - a common example is to use inheritance when you really should be using aggregation - but that doesn't mean that OO as a whole is a bad idea, it just means you need to understand what it does before trying to use it.
09 Apr, 2010, David Haley wrote in the 33rd comment:
Votes: 0
Shas, as an OOP jihadi, are you as knowledgeable about every other paradigm that you jihad against? :wink:


And of course, nobody is claiming that every tool in every toolbox will be appropriate for every situation. But I think that some people are arguing that not every toolbox has a tool most appropriate for some situations.
09 Apr, 2010, Runter wrote in the 34th comment:
Votes: 0
David Haley said:
Shas, as an OOP jihadi, are you as knowledgeable about every other paradigm that you jihad against? :wink:


And of course, nobody is claiming that every tool in every toolbox will be appropriate for every situation. But I think that some people are arguing that not every toolbox has a tool most appropriate for some situations.


Jihad only requires faith.
09 Apr, 2010, flumpy wrote in the 35th comment:
Votes: 0
I think it's worth pointing out that there can be cross over between oo and non oo paradigms. As I stated previously it is perfectly valid to use functional decomposition in an object, but obviously if you start finding yourself getting into code smell situations, like your object doing too much, then a little OO refacoring probably wouldn't go amiss.

Tbh, I don't think there are any square pegs that cannot be rounded with an OO language. I think it's slightly unfair to say that OO could not solve the same kinds of problems non-OO languages could, even with mud programming. The problems have obviously been solved to a good degree by looking at the number of mud bases that are out there. Take-up of those OO mud bases is obviously another issue altogether :)
09 Apr, 2010, David Haley wrote in the 36th comment:
Votes: 0
People aren't talking about merely solving problems. Recall that any Turing-complete language can solve any problem that any other Turing-complete language can solve. So yes, you can round any square peg using your favorite assembly language. The point had to do with which solutions work better than others in certain circumstances.

But while you say there can be crossover, I get the impression that others say there isn't any. :shrug:
09 Apr, 2010, Kayle wrote in the 37th comment:
Votes: 0
Well, looks like this thread has picked up again. So I figured I'd give a bit of an update on what I'd decided to do to tackle it. Bear in mind that this is a work in progress, is no where near the completed product, and will likely be revised several more times before it actually starts to really work.

Also, it's long. >.>
class Chassis
{
private:
Chassis( const Chassis & );
Chassis &operator = ( const Chassis & );

public:
Chassis( );
~Chassis( );

private:
std::string m_Name; // Name of the Chassis, ex: Seinar Fleet Systems TIE Interceptor
/*
* 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
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:
// …Maybe these should be in class Ship?
int currentMass; // Current Mass the Chassis is carrying
int currentHull; // Current Hull Value


};

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

public:
Armor( );
~Armor( );

private:
std::string m_Name; // Name of the Armor, ex: Gallofree Mark I Starfighter Armor
/*
* 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;
/*
* 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:

};

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

public:
Booster( );
~Booster( );

private:
std::string m_Name; // Name of the Booster, ex: Corellian Engineering Corp. TJH-3 Starship Boosters
/*
* 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;
/*
* 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;
/*
* 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:

};

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

public:
Capacitor( );
~Capacitor( );

private:
std::string m_Name; // Name of the Capacitor, ex: Incom BJN-825 Capacitor
/*
* 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;
/*
* 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;
/*
* 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:

};

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

public:
Reactor( );
~Reactor( );

private:
std::string m_Name; // Name of the Reactor, ex: Mark I Fusion Reactor
/*
* 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;
/*
* 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:

};

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

public:
Engine( );
~Engine( );

private:
std::string m_Name; // Name of the Engine, ex: Mark I Starfighter Engine
/*
* 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;
/*
* 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;
/*
* 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;
/*
* 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.
*/
int m_Maneuverability;

public:

};

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

public:
ShieldGenerator( );
~ShieldGenerator( );

private:
std::string m_Name; // Name of the Shields, ex: Armek Plasma Shell Deflector Shields
/*
* 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;
/*
* 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;
/*
* 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:

};

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

public:
Weapon( );
~Weapon( );

private:
std::string m_Name; // Name of the Weapon, ex: Corellian Cruiser Grade Mark I Blaster
/*
* 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;
/*
* 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;
/*
* 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:

};

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

public:
Launcher( );
~Launcher( );

private:
std::string m_Name; // Name of the Launcher, ex: Cygnus Mark I Proton Torpedo Launcher
/*
* 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;
/*
* Refire Rate is the rate at which the launcher cycles and is ready to fire again.
*/
int m_RefireRate;
/*
* This is the type of ordinance a given Launcher would deploy.
* Examples: Proton Torpedo, Concussion Missile, Space bomb, Seismic Torpedo
*/
short m_Type;

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

};

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

public:
CMLauncher( );
~CMLauncher( );

private:
std::string m_Name; // Name of the Launcher, ex: Cygnus Mark I Chaff Deployer
/*
* 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;
/*
* 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;
/*
* Refire Rate is the rate at which the launcher cycles and is ready to fire again.
*/
int m_RefireRate;
/*
* 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;

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

// get functions
const char *getName( ) const { return m_Name.c_str( ); }
short getType( ) const { return m_Type; }
int getMass( ) const { return m_Mass; }
int getComponentHP( ) const { return m_ComponentHitPoints; }
int getRefireRate( ) const { return m_RefireRate; }

// set Functions
void setName( const char *name )
{
if( !m_Name.empty( ) )
m_Name.clear( );
m_Name = name;
}
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; }
void setComponentHP( int hp ) { m_ComponentHitPoints = hp; }
void setRefireRate( int rate ) { m_RefireRate = rate; }

};

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

public:
Ordinance( );
~Ordinance( );

private:
std::string m_Name; // Name of the Ordinance, ex: Mark I Proton Torpedo
/*
* 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;
/*
* This is the type of ordinance.
* Examples: Proton Torpedo, Concussion Missile, Space bomb, Seismic Torpedo
*/
short m_Type;
/*
* 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
const char *getName( ) const { return m_Name.c_str( ); }
int getMass( ) const { return m_Mass; }
short getType( ) const { return m_Type; }

// set Functions
void setName( const char *name )
{
if( !m_Name.empty( ) )
m_Name.clear( );
m_Name = name;
}
void setMass( int mass ) { m_Mass = mass; }
void setType( short type )
{
if( type >= MAX_ORDINANCE || type < 0 )
{
bug( "%s: Invalid ordinance passed: %d", __FUNCTION__, type );
return;
}
else
m_Type = type;
}

};

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

public:
CounterMeasure( );
~CounterMeasure( );

private:
std::string m_Name; // Name of the Counter Measure, ex: Mark I Micro-Chaff
/*
* 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;
/*
* This is the type of counter measure.
* Examples: Chaff, Micro-Chaff, EM Emitters, Sensor Decoys, IFF Confusers
*/
short m_Type;

public:
// get functions
const char *getName( ) const { return m_Name.c_str( ); }
int getMass( ) const { return m_Mass; }
short getType( ) const { return m_Type; }

// set Functions
void setName( const char *name )
{
if( !m_Name.empty( ) )
m_Name.clear( );
m_Name = name;
}
void setMass( int mass ) { m_Mass = mass; }
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;
}

};

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;

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.

// 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; }

// set Functions
void setName( const char *name )
{
if( !m_Name.empty( ) )
m_Name.clear( );
m_Name = name;
}
void setFilename( const char *name )
{
if( !m_Filename.empty( ) )
m_Filename.clear( );
m_Filename = name;
}
void setOwner( const char *name )
{
if( !m_Owner.empty( ) )
m_Owner.clear( );
m_Owner = name;
}
void setPilot( const char *name )
{
if( !m_Pilot.empty( ) )
m_Pilot.clear( );
m_Pilot = name;
}
void setCoPilot( const char *name )
{
if( !m_CoPilot.empty( ) )
m_CoPilot.clear( );
m_CoPilot = name;
}

// Misc
bool isRental( );
bool rent( Character *ch );
bool checkPilot( Character *ch );

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

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

// Damage
void destroy( Character *ch, const char *reason );
void damage( int min, int max );

// Movement
void toSpace( );
void launch( );
void land( const char *pad );
void drive( Character *ch, Exit *ex, int fall );


// 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( System *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 ); }
};


Side note: The syntax highlighting in the code box is horrible, would it be possible to get some highlighting similar to that at pastebin.com?
09 Apr, 2010, David Haley wrote in the 38th comment:
Votes: 0
Two brief comments:
void setName( const char *name )

You should probably take a const std::string& here; a std::string can be constructed from a const char*, however if you only take const char* then any time you want to call this with a std::string you'll have to .c_str() it, which will get annoying quickly…

if( !m_Name.empty( ) )
m_Name.clear( );
m_Name = name;

You don't need to clear the string before assigning a new value; the assignment operator does the right thing.
10 Apr, 2010, Kayle wrote in the 39th comment:
Votes: 0
I won't ever be passing a std::string to it though. Because arguments are passed around as const char* not std::string. The only place std::string is used anywhere in the code base in question is inside the various classes. Which won't be using the get/set stuff anyway, because they can directly access their own private data. I haven't gotten around to, nor do I have plans to, convert the rest of the code base en masse to std::string. In the event that things get converted, I'll update it to std::string&.

As for the clear() thing, Force of habit from dealing with STRALLOC/STRFREE and str_dup/DISPOSE…
10 Apr, 2010, David Haley wrote in the 40th comment:
Votes: 0
Well, it doesn't hurt to give yourself the optionality now and it saves you from having to go back later and change a bunch of stuff. Who knows what the code might look like later? It could be getting more complex, and hence more and more annoying to change.
20.0/80