Work in Progress
This article (Unit Coding), or a section of this article, is under construction; please excuse all cranes, dozers, and MCVs in the meantime. Feel free to add additional content to the page, including pictures and templates; the final page may differ from the current page to a large extent.
The purpose of unit coding is to devise the necessary code that tells the RA3 engine how a unit should behave in game, and what artwork assets that the game needs to use to create that unit. This article gives information about this proccess, as well as showing many of the coding techniques and practises that devs use to code Paradox.
Coding Basics Edit
Every unit in RA3 and Paradox needs a number of XML files that tell the engine how that unit behaves. An XML file is basically a text document designed to be read by an application (in this case, the RA3 SDK). XML is a "markup" language that simply stores information. It isn't a coding language (such as python, or perl, or c++ which RA3 is probably written in), in that it doesn't pass instructions to the computer's proccessor. It just holds data that the RA3 SDK, and RA3 itself will know what to do with. As a result, you cannot pass commands to RA3 if it doesn't know what to do with them, meaning there are limitations as to what can be achieved with the SDK. There is a large amount of documentation about how XML works on the internet, although there is only a limited amount of information about how it applies to RA3.
Paradox Coding Edit
The following sections are examples of code, and methods that the paradox team have managed to implement.
Working with models Edit
Showing and Hiding Model Bones Edit
Often, when you are coding a unit, you need to hide certain parts of the 3D model under certain conditions. For example, production structures have certain indicators, which only show if they are actively producing. If this is not the case, the bones for these parts are hidden.
Another commonly used instance is in heroic weapons, which cannot be shown until the unit reaches heroic veterancy level. In this section, I am demonstrating different ways of showing and hiding model bones, using the Confederation Frigate's heroic weapon as an example. (please note: this is only showing the graphical aspect of heroic weapons. The following code does not activate the actual weapon)
Once the basic model was ingame, I had to implement a method that hid the bones for the heroic weapon (a pair of rocket-pods) until heroic level is attained, at which point the standard weapon (a large flak cannon) disappears.
First off, I use an AnimationState to add or remove the bones I don't want. Despite the fact that the Frigate has no animation, I can still use the AnimationState to add or remove bones.
<AnimationState ParseCondStateType="PARSE_DEFAULT"> <Script> CurDrawableHideSubObjectPermanently("MISSILETURRET") CurDrawableHideSubObjectPermanently("ROCKETTUBE") </Script> </AnimationState>
I could have added the following to tell the Frigate to show the flak bones, but all bones start visible by default, so this wasn't needed.
CurDrawableShowSubObjectPermanently("FLAKTURRET") CurDrawableShowSubObjectPermanently("FLAK_01") CurDrawableShowSubObjectPermanently("FLAK_02")
Then, I use a SubObjectsUpgrade, in order to change this upon achieving heroic status.
<SubObjectsUpgrade id="ModuleTag_Veterancy_1"> <TriggeredBy>Upgrade_Veterancy_HEROIC</TriggeredBy> <Nugget HideSubObjects="FLAKTURRET FLAK_01 FLAK_02" ShowSubObjects="MISSILETURRET ROCKETTUBE" Upgrade="Upgrade_Veterancy_HEROIC"/> </SubObjectsUpgrade>
This is placed in the Behavious section of the unit's xml. As you can see, once Upgrade_Veterancy_HEROIC is attained, it tells the Frigate to hide the Flak bones, and to show the rocket-pod bones.
This implementation achieves the purpose. On testing, the Frigate starts with the flak cannon, and changes weapon once receiving heroic status.
However, with the AnimationState above, you may notice that it is set as PARSE_DEFAULT. For our purposes, this basically means that this will be used when no other AnimationStates are being used. In addition, the Script is only run when first activated. This works for our current example, but with a slight change of code, it will no longer work.
<AnimationState ParseCondStateType="PARSE_NORMAL" ConditionsYes="FIRING_B"> <Script> CurDrawableHideSubObjectPermanently("MISSILECOVER_01") </Script> </AnimationState>
Soon later, I added the above piece of seemingly unrelated code. This was one of the four missile covers, which I wanted to disappear when it was firing a weapon. In order to make the covers reappear afterwards, I edited the original AnimationState.
<AnimationState ParseCondStateType="PARSE_DEFAULT"> <Script> CurDrawableHideSubObjectPermanently("MISSILETURRET") CurDrawableHideSubObjectPermanently("ROCKETTUBE") CurDrawableShowSubObjectPermanently("MISSILECOVER_01") CurDrawableShowSubObjectPermanently("MISSILECOVER_02") CurDrawableShowSubObjectPermanently("MISSILECOVER_03") CurDrawableShowSubObjectPermanently("MISSILECOVER_04") </Script> </AnimationState>
This told the covers to reappear when the alternate AnimationState no longer applied. Once built and ran, the missile covers worked perfectly. However, I was now having a problem with my original MISSILETURRET and ROCCKETTUBE bones.
Now that there was another AnimationState, control would change from the default one, and move temporarily to the alternate FIRING_B AnimationState. When the original AnimationState took effect again, it would rerun the HideSubObject code, making the MISSILETURRET and ROCKETTUBE bones disappear again.
After consideration, I removed the MISSILETURRET and ROCKETTUBE entries from the default AnimationState. Now that the AnimationStates were being used by things other than the heroic weapon, it had made it impossible to use. Instead, I made another SubObjectsUpgrade.
<SubObjectsUpgrade id="ModuleTag_Faction_1"> <TriggeredBy>Upgrade_AlliesFaction</TriggeredBy> <Nugget ShowSubObjects="FLAKTURRET FLAK_01 FLAK_02" HideSubObjects="MISSILETURRET ROCKETTUBE" Upgrade="Upgrade_AlliesFaction"/> </SubObjectsUpgrade>
As you can see, since I no longer had the heroic upgrade to set as the trigger for the SubObjectsUpgrade, I instead used Upgrade_AlliesFaction. This meant that when the unit is created, provided it belongs to the Allied faction (When I was testing this, I was testing it under the Allied faction) it will hide the heroic weapon, while keeping the standard flak cannon. I could then add a similar SubObjectsUpgrade module for each faction, should a team other than Allies manage to get a Frigate.
This modification achieved the purpose. It hid the Missile bones at the start, but upon reaching heroic, they were enabled, and the flak cannon disabled. In addition, the Missile Cover bones still worked.
In order to add a new weapon to your unit, you will have to create a Weapon.xml file where all new weapons you create are stored. As ever, the best way to do this is to copy the weapon.xml file in the Sample Mod. When you open it up, it should have lots of info about the Harbinger Gunship's weapons. For an example, I will be creating the Thermobaric cannons for the Lucifer Track.
Now before you fiddling around with the different directives inside there, it is sometimes best to plan out what the weapon will be like. The Lucifer has Thermobaric Cannons which are effective against all ground units but cannot hit subs. Since Thermobaric shells explode we can assume that the weapon will have a small AoE. Since it is a superheavy it will reload slowly but do a lot of damage.
Now to actually build the weapon. First, delete everything from
to the bottom just before the </AssetDeclaration> text.
Now we will edit the WeaponTemplate.
Here are the parts relevant to our weapon.
id="AlliedGunshipAircraftWeapon AttackRange="400.0" MinimumAttackRange="50" CanFireWhileMoving="true" ClipSize="12" RadiusDamageAffects="ENEMIES" FireFX="FX_Allied_Gunship_Fire" FireVeteranFX="FX_Allied_Gunship_Fire" FireSound="ALL_Gunship_ColliderCannonWeaponFire" Flags="ATTACK_NEEDS_LINE_OF_SIGHT" WeaponSpeed="500" MinWeaponSpeed="500" MaxWeaponSpeed="500" RequiredAntiMask="ANTI_GROUND ANTI_STRUCTURE ANTI_WATER">
MinSeconds="2.0s" MaxSeconds="2.0s" />
MinSeconds="2.0s" MaxSeconds="2.0s" /></nowiki>
Thats a lot of info but most of it is self explanatory;
- The ID is the name of the weapon and what you have to call it when referencing it in another file. I'll just call it LuciferCannon.
- Attack Range,Minimum Attack range and Can Fire While Moving speak for themselves really. I'll make them 350, 10 and true.
- Clipsize is the number of shots that can be fired before reloading. I'll make it 1.
- Radius Damage effect is who is affected by the units AoE attack. I'll make it ENEMIES
- Obviously, since we haven't made any Fx or sounds for this unit we can't put in our own Fx and sounds so lets just ignore it for now.
- Flags are used to designate things not mentioned in other places. It's basically a Misc box. The two main ones involved with weapons are ATTACK_NEEDS_LINE_OF_SIGHT and NOT_ATTRACTED_BY_MAGNETS. The first one is whether the weapon can fire over walls or not. I'm not sure about this but I think that the second is used to show if the weapon is diverted by the Assault Destroyer's black hole armour.
- Weapon Speed is the time it takes for a weapons shot to hit a target. It can be set at 999999 if you want it to be instantaneous. I'll set it to 1000 so the shell doesn't hit instantly.
- The RequiredAntiMask designates what this weapon can hit. I'll make it ANTI_WATER ANTI_GROUND ANTI_STRUCTURE.
- Firing Duration is the amount of time it takes to fire a weapon.
- Clip Reload time is so obvious I'm not even going to tell you what it is. If you can't work it out, go outside and play on the swings like the 5 year old you are.