Command & Conquer™ Remastered Collection: How to Add a New Unit for Tiberian Dawn

Hello everyone! In this topic we will explain how to add a new unit for Tiberian Dawn in C&C: Remastered Collection. And, as a sample, we will use this Nuke Tank as sample, since Petroglyph Games has properly documented each step done to add this unit and tested it extensively.

“What would the Brotherhood of Nod do if they captured the Mammoth Tank?”  Well, one guess is they’d replace the turret with a giant artillery cannon, and have it fire tactical nukes!  Thus, the Nuke Tank was born.


So, let’s show how they have coded this Nuke Tank, shall we?

First of all, all units in Tiberian Dawn are declared inside the DLL, so you will need appropriate tools (Visual Studio compatible IDE) to open the source code and compile it. We recommend you to follow the instructions of this topic to do it. Once you are able to compile the source code, let’s move on:

1) Define the constants for your new unit.

At the file DEFINES.H, you will need to defines constants that you will use to refer to your unit, projectile, weapon type, voice and few other entities that you may need to create to allow your unit to work as you desire. In Nuke Tank’s case, we will only define labels for the unit, projectile, weapon type and voice (sound effect played when you select the unit or order it to do something).

At the typedef enum UnitType, you should define a new constant to your unit before UNIT_COUNT, as seen in bold.


UNIT_STEG, // Stegasaurus
UNIT_NUKE_TANK, // Mammoth with a nuke

At the typedef enum BulletType, you should define a new constant to your projectile before BULLET_COUNT, as seen in bold.

BULLET_TREXBITE, // Tyrannosaurus Rex’s bite – especially bad for infantry
BULLET_NUKE_LOB, // Nuke projectile

At the typedef enum WeaponType, you should define a new constant to your weapon before WEAPON_COUNT, as seen in bold.


At the typedef enum VocType, you should define a new constant to your voice sound effect before VOC_COUNT, as seen in bold.

VOC_DINODIE1, // Dino die sound.
VOC_NUKE_LOB, // Modded unit firing sound


Now that we have labels to refers to these entities, it is time to define them and their attributes.

2) Let’s start with the voice, since the voice doesn’t require anyone else.

You will need to define its attributes at AUDIO.CPP, by adding a new element at the end of SoundEffectName[VOC_COUNT].

You will define the name of the sound file that it plays, the priority and the context. The context is explained above it in ContextType.

In our case, we have:

{“NUKE_LOB”, 10, IN_NOVAR} // VOC_NUKE_LOB Mod expansion unit firing sound

So, you must have a sound file called NUKE_LOB, and your sound has a priority of 10 and it won’t have any variations (IN_NOVAR), no effects applied to it of any kind.

Be aware that the sounds listed at SoundEffectName[VOC_COUNT] must be in the same exact order of VocType in DEFINES.H.


3) Now let’s do the bullet, also known as projectile. The bullet is a class, since it is more complex and allow more options. It is declared at BBDATA.CPP.

So, we need to declare it as a class NukeLob of the type BulletClass. You can copy any other class based on BulletClass and modify its properties, like what we will do below:

static BulletTypeClass const NukeLob(
“BOMB”,                  // NAME:         Text name of this unit type.
true,                  // Flies over tall walls?
false,               // Homes in on target?
true,                  // Projectile arcs to the target?
false,               // Is this a dropping bomb-like object?
false,               // Is this projectile invisible?
false,               // Will it blow up even if it gets just NEAR to target?
false,               // Does it have flickering flame animation?
false,               // Can it run out of fuel?
true,                  // Is there no visual difference between projectile facings?
true,                  // Is projectile inherently inaccurate?
false,               // Translucent colors are used?
false,               // Good against aircraft?
0,                        // ARMING:      Time to arm projectile after launch.
0,                        // RANGE:      Inherent override range factor.
MPH_MEDIUM_FAST,         // SPEED:      Miles per hour.
0,                        // ROT:         Rate of turn (degrees per tick).
WARHEAD_HE,               // WARHEAD:      If fires weapon, warhead type
ANIM_ATOM_BLAST         // Explosion to use upon impact.

So we have a class called NukeLob that inherits BulletClass and it has all the attributes documented above. If anyone wants me to explain each attribute, it will be done in a later version of this tutorial.

And finally, in order to allow the game to use this class, it must know about it at the same BBDATA.CPP, there is an array of pointers below called BulletTypeClass::Pointers[BULLET_COUNT]. You should add a pointer to the NukeLob class at the end of the list. Like this:



4) We have the voice and we have the projectile. With these two classes ready, we can move to the weapon type. Due to optimization reasons (game performance), you must add it at CONST.CPP. There is a const for it known as WeaponTypeClass const Weapons[WEAPON_COUNT] where all projectiles are defined. You should add yours at the end of the declaration and provide the following attributes respectively: Damage, Rate of Fire, Range, Sound, and Animation.

So, in this sample:


BULLET_NUKE_LOB does 150 hit points of damage, takes 130 ticks to reload, is the weapon with the highest range of the game, plays the WEAPON_NUKE_LOB sound, and plays the ANIM_MUZZLE_FLASH animation (which is the tank firing animation seen in Light, Medium Tank and few other units).

Be aware that the weapons listed at the Weapons[WEAPON_COUNT] must be in the same exact order of WeaponType in DEFINES.H.


5) Now that the weapon is ready… we need to build the main thing, our main objective here: the unit! The bloody Nuke Tank, bout time! So, let’s move to UDATA.CPP.

The unit willl be a class that will inherit UnitTypeClass. And there you will have a myriad of attributes to define. So, copy and paste another unit that is as similar as possible to yours and modify its attributes, like this:

// Nuke tank
static UnitTypeClass const UnitNukeTank(
TXT_HTANK,                        // NAME:         Text name of this unit type.
“NTNK”,                           // NAME:         Text name of this unit type.
ANIM_ART_EXP1,                     // EXPLOSION:   Type of explosion when destroyed.
7,                        // Build level.
STRUCTF_TEMPLE,         // Building prerequisite.
true,            // Can this be a goodie surprise from a crate?
true,            // Is a leader type?
false,         // Only has eight facings?
false,         // Always use the given name for the vehicle?
false,         //   Is this a typical transport vehicle?
false,         // Can it be crushed by a heavy vehicle?
true,            // Can this unit squash infantry?
false,         // Does this unit harvest Tiberium?
false,         // Is invisible to radar?
true,            // Is selectable by player?
true,            // Can it be a target for attack or move?
false,         // Is it insignificant (won’t be announced)?
false,         // Is it immune to normal combat damage?
true,            // Is it equipped with a combat turret?
false,         // Fires multiple shots in quick succession?
true,            // Can it be repaired in a repair facility?
true,            // Can the player construct or order this unit?
true,            // Is there a crew inside?
false,         // Does it have a rotating radar dish?
false,         // Is there an associated firing animation?
false,         // Must the turret be in a locked down position while moving?
true,            // Does it lay tracks while moving?
true,            // Is this a gigundo-rotund-enormous unit?
false,         // Is the unit’s art as “chunky” cardinal facing only?
false,         // Is the unit capable of cloaking?
false,         // Does the unit have a constant animation?
-1,                              // AMMO:         Number of shots it has (default).
600,                              // STRENGTH:   Strength (in damage points).
6,                                 // SIGHTRANGE:   Range of sighting.
1500,                              // COST:         Cost to build (Credits).
10,                              // SCENARIO:   Starting availability scenario.
80,80,                            // RISK/RWRD:   Risk/reward rating values.
HOUSEF_BAD,                        // OWNABLE:      Ownable by house (bit field).
ARMOR_STEEL,                     // ARMOR:      Armor type
SPEED_TRACK,                     // MOVE:         Locomotion type.
MPH_KINDA_SLOW,                  // SPEED:      Miles per hour.
3,                                 // ROT:         Rate of turn (degrees per tick).
0,                                 // Turret center offset along body centerline.
MISSION_HUNT                     // ORDERS:      Default order to give new unit.

Most things there are explained by Petroglyph/Westwood’s documentation, so I will focus on few things above.

The house/faction that this unit belongs is defined by HOUSEF_MULTI1 | HOUSEF_MULTI2 | HOUSEF_MULTI3 | HOUSEF_MULTI4 | HOUSEF_MULTI5 | HOUSEF_MULTI6 | HOUSEF_JP | HOUSEF_BAD. Except for HOUSEF_BAD, everything else is mere formality that does not deny any player from building it nor the “Disaster Containment Team” to own it… but the real deal is HOUSEF_GOOD means that GDI can build it and HOUSEF_BAD means that Nod can build it.

The weapons are set right after the houses. WEAPON_NUKE_LOB,WEAPON_NONE means that primary weapon is WEAPON_NUKE_LOB and that there is no secondary weapon.

And just like the weapons, you need to add the pointer to this class at UnitTypeClass::Pointers[UNIT_COUNT].

   &UnitNukeTank,      // UNIT_NUKE_TANK

The order of these pointers must be syncronized with UnitType typedef in DEFINES.H.

And for those wondering, where do we declare the graphics? By default, it uses the TEXT attribute in the class declaration above, but our Nuke Tank reuses the image from Mammoth Tank. So, Petroglyph has done a quick “workaround” to handle this issue by editing the OneTime method at UnitTypeClass, at void UnitTypeClass::One_Time(void) in UDATA.CPP.

So, before defining:

      ((void const *&)uclass.ImageData) = ptr;

They’ve set this ptr into something else specifically if the unit is the UNIT_NUKE_TANK.

** Need some kind of shape data for our modded unit
if (index == UNIT_NUKE_TANK && ptr == NULL) {
_makepath(fullname, NULL, NULL, “HTNK”, “.SHP”);
ptr = MixFileClass::Retrieve(fullname);


Finally, our nuke tank has a turret. And we need to determine its fire coordinates. I mean, where will the projectile be created when it fires? So, in TURRET.CPP, we will have to edit the Fire_Coord method of TurretClass. More specifically for searching purposes: COORDINATE TurretClass::Fire_Coord(int which) const

Right after the other units get mentioned, you should add:

      case UNIT_NUKE_TANK:
coord = Coord_Move(coord, DIR_N, 0x00A0);
dist = 0x00A0;

In case the unit is UNIT_NUKE_TANK, the coordinate will be 240 leptons (?) above the center of the turret.


6) Final remarks:

And that’s it. Now that you have the code ready, it is time to compile it and test it out.

Remember that Nuke Tank is properly documented by Petroglyph Games and they’ve separated its code between #ifdef PETROGLYPH_EXAMPLE_MOD and #endif. That’s a good coding manners, since if it is something goes wrong, all you need to do is to disable PETROGLYPH_EXAMPLE_MOD (in this sample) or whaever you define. Also, thanks to this coding manners, we could figure out how to create a unit without being able to play the game yet.

I know that you won’t code a unit in this order. You will certainly think of a unit and, if it needs a weapon, a projectile or a voice, you will code these things later. It is ok and intuitive to do that instead. The order we have used here allows you to test the code as you do each step.

So, happy modding and have fun!

By Banshee

Related Posts:

Post Author: Robins Chew

Leave a Reply

Your email address will not be published. Required fields are marked *