inheritance examples

This is a moderated forum that collects tutorials, guides, and references for creating Transcendence extensions and scripts.
Post Reply
relanat
Militia Captain
Militia Captain
Posts: 768
Joined: Tue Nov 05, 2013 9:56 am

Tue May 28, 2019 4:22 am

Code can be confusing. Often the code isn't where you would think it should be. An example is the starting Sapphire playership. It has a basic cargo space of 50 tons. This can be seen in the playership select screen as you start a game.

Here is the ShipClass UNID code for that playership from 'PlayerShips.xml' in 'Transcendence_Source':

Code: Select all

<ShipClass UNID="&scSapphirePlayer;"
		manufacturer=		"Zubrin Systems"
		class=				"Sapphire"
		type=				"yacht"
		attributes=			"commonwealth, playerClass, startingClass, zubrin, 00200000_PlayerShip"
		inherit=			"&scSapphireYacht;"
		characterClass=		"&unidPilgrimClass;"
		>

	<!-- Configuration -->
		
	<Armor
		armorID=			"&itReactiveArmor;"
		count=				"4"
		/>
		
	<Devices>
		<Device deviceID="&itRecoillessCannon;"/>
		<Device deviceID="&itClass1Deflector;"/>
		<Device deviceID="&it15MWReactor;"/>
	</Devices>

	<Items>
		<Item count="4d6" item="&itHelium3FuelRod;"/>
	</Items>

	<!-- Player Settings -->
		
	<PlayerSettings
		desc=			"The versatile Sapphire yacht strikes a good balance between a gunship and a freighter."
		sortOrder=		"10"
		initialClass=		"true"
		startingCredits=	"10d100+1500"
		/>
</ShipClass>
There is no mention of cargo space anywhere here. But the playership does has a 50 ton cargo hold. So how does the game know?

The sixth line of code reads:

Code: Select all

inherit=	"&scSapphireYacht;"
'inherit' is a code feature which allows different UNIDs to share code. This reduces the total amount of code in the game so it is smaller, can load and run faster and provide better features for us all to enjoy.
How it works is that the playership UNID, &scSapphirePlayer;, 'inherits' all of the code from this other UNID, &scSapphireYacht;.

Here is that UNID from 'CommonwealthShips.xml', also in 'Transcendence_Source'. It is fairly long because it contains all of the code needed by Sapphire ships:

Code: Select all

<ShipClass UNID="&scSapphireYacht;"
		manufacturer=		"Zubrin Systems"
		class=				"Sapphire"
		type=				"yacht"
		defaultSovereign=		"&svCommonwealth;"
		attributes=			"commonwealth, genericClass, zubrin"
		inherit=			"&baHumanTechShip;"
		>

	<Hull
		size=				"31"
		mass=				"30"
		cargoSpace=			"50"

		maxReactorPower=		"10000"
		maxCargoSpace=		"150"
		maxDevices=			"8"

		maxArmor=			"heavy"
		stdArmor=			"medium"
		/>

	<Drive
		maxSpeed=			"20"
		thrust=				"300"
		powerUse=			"20"
		/>

	<Maneuver
		maxRotationRate=		"9.0"
		rotationAccel=		"2.0"
		rotationStopAccel=		"9.0"
		/>

	<DeviceSlots>
		<DeviceSlot criteria="w +property:omnidirectional;" posAngle="0" posRadius="13"/>
		<DeviceSlot criteria="w" posAngle="0" posRadius="22"/>
	</DeviceSlots>

	<!-- Configuration -->

	<Armor
		armorID=			"&itReactiveArmor;"
		count=				"4"
		/>
		
	<Devices>
		<Device deviceID="&itRecoillessCannon;"/>
		<Device deviceID="&itClass1Deflector;"/>
	</Devices>

	<!-- Treasure -->

	<Items>
		<Item count="4d6" item="&itHelium3FuelRod;"/>
	</Items>

	<!-- Image and Effects -->

	<Image imageID="&rsSapphire;" imageWidth="62" imageHeight="62"	rotationCount="120" rotationColumns="12" viewportRatio="0.00475"/>
	<HeroImage imageID="&rsZubrinLarge;" imageWidth="320" imageHeight="320"/>

	<Effects>
		<Effect type="thrustMain"		posAngle="160"	posRadius="7"	posZ="0"	rotation="180"	bringToFront="*"/>
		<Effect type="thrustMain"		posAngle="-160"	posRadius="7"	posZ="0"	rotation="180"	bringToFront="*"/>
	</Effects>

	<!-- Player Settings -->

	<PlayerSettings>
		<ArmorDisplay>
			<ShipImage imageID="&rsArmorHUDShip_Sapphire;" imageWidth="136" imageHeight="136"/>
				
			<ArmorSection name="forward"
					segment="0"
					imageID="&rsZubrinArmor;" 
					imageX="0" imageY="0" imageWidth="52" imageHeight="29"
					destX="42" destY="15" hpX="55" hpY="14"
					nameY="8" nameBreakWidth="200" nameDestX="0" nameDestY="10" />

			<ArmorSection name="starboard"
					segment="3"
					imageID="&rsZubrinArmor;" 
					imageX="52" imageY="0" imageWidth="22" imageHeight="59"
					destX="92" destY="45" hpX="95" hpY="60"
					nameY="30" nameBreakWidth="360" nameDestX="12" nameDestY="0" />

			<ArmorSection name="port"
					segment="1"
					imageID="&rsZubrinArmor;" 
					imageX="142" imageY="0" imageWidth="22" imageHeight="59"
					destX="22" destY="45" hpX="15" hpY="60"
					nameY="52" nameBreakWidth="200" nameDestX="0" nameDestY="8" />

			<ArmorSection name="aft"
					segment="2"
					imageID="&rsZubrinArmor;" 
					imageX="74" imageY="0" imageWidth="68" imageHeight="14"
					destX="34" destY="103" hpX="55" hpY="105"
					nameY="74" nameBreakWidth="360" nameDestX="12" nameDestY="0" />
		</ArmorDisplay>
	</PlayerSettings>

	<!-- AI and Behavior -->

	<AISettings
		fireRateAdj=		"30"
		fireAccuracy=		"90"
		perception=		"4"
			
		combatStyle=		"advanced"
		/>
</ShipClass>
All of this code here is used in the playership UNID even though the code isn't actually in the playership UNID. Looking in the <Hull> section of code we can see the 'cargoSpace' value is equal to "50" here. Because the playership UNID 'inherit's all of this code it will use the 'cargoSpace="50"' code and also have a cargo space of 50 tons.

============================

So a ship can inherit code from another UNID. Importantly, this can happen multiple times.
The Sapphire yacht UNID also inherits code. But from a different UNID. The seventh line of code is this:

Code: Select all

inherit=	"&baHumanTechShip;"
Lets look at the code for this UNID, these types of UNIDs are commonly called base classes. I've deleted some of the comments to make it a bit smaller:

Code: Select all

<ShipClass unid="&baHumanTechShip;"
		virtual="true">

	<ShipStandard
		drivePowerUse=	"20"
		minHullPrice=	"2000"
		pricePerHullPoint=	"110"
		/>

	<!-- Player Settings -->

	<PlayerSettings>
		<ArmorDisplay 
			style=			"circular"
			/>

		<ShieldDisplay shieldLevelEffect="&efShieldHUDDefault;">
		</ShieldDisplay>

		<ReactorDisplay>
			<Image imageID="&rsZubrinReactor;" 
					imageX="0" imageY="0" imageWidth="256" imageHeight="60"/>

			<PowerLevelImage imageID="&rsZubrinReactor;"
				imageX="0" imageY="60" imageWidth="202" imageHeight="14"
				destX="54" destY="9"/>

			<FuelLevelImage imageID="&rsZubrinReactor;"
					imageX="0" imageY="74" imageWidth="194" imageHeight="14"
					destX="54" destY="37"/>

			<FuelLowLevelImage imageID="&rsZubrinReactor;"
					imageX="0" imageY="88" imageWidth="194" imageHeight="14"/>

			<ReactorText x="62" y="22" width="154" height="14"/>
			<PowerLevelText x="62" y="0" width="154" height="9"/>
			<FuelLevelText x="62" y="51" width="154" height="9"/>
		</ReactorDisplay>
	</PlayerSettings>

	<!-- Events and Behavior -->

	<Events>
		<CanUpgradeTo>
			True
		</CanUpgradeTo>

		<CanUpgradeFrom>
			True
		</CanUpgradeFrom>
	</Events>
</ShipClass>
Of note here is the <ArmorDisplay> code. It uses a value of 'style="circular"'. So any ship which inherits from this UNID will have a circular armor display at the bottom right of the screen during gameplay.
But the Sapphire doesn't use the circular display. It has a special display which matches the Sapphire's shape. So somehow the armor display has been changed.
We need to note here that this UNID doesn't inherit from any other UNIDs. So all of this code will always be the same. But using the inheritance feature does allow us to change the code.if we want.

It does this by overwriting code.
When the game starts it checks each UNID. For the Sapphire yacht UNID it first 'inherit's from &baHumanTechShip;. So all of the code from there is loaded. This includes the "circular" armor display code. Then it checks the rest of the code in the Sapphire yacht UNID. Because there is also <ArmorDisplay> code there it uses that instead of the "circular" code it has inherited. It gives priority to the newer code. This is called overwriting or overriding.

When the game checks the playership UNID it inherits all of the code from &scSapphireYacht; and uses that for the playership. Because there isn't any new <ArmorDisplay> code in the playership UNID it will use the code it has inherited. This includes the armor code which was overwritten after it was inherited from the base class.
So although the playership did inherit the "circular" code from the base class, via the yacht code, it will use the special Sapphire armor display because the newer code overwrote the "circular" code and there is no newer code to overwrite this.


We can double check this by looking at one of the Centurion playerships available at ship brokers. These use the "circular" armor display.
The Block 2 Centurion playership, &scCenturionBlock2L6;, inherits from &scCenturionBlock2; which inherits from &baHumanTechShip;.
The code for this Centurion playership is at the top of 'StdPlayerShips.xml' in 'Trancendence_Source'.
In this example the armor display code for this ship comes from the base class right at the end of the inheritance chain. Because neither &scCenturionBlock2L6; or &scCenturionBlock2; contains any <ArmorDisplay> code, the "circular" code does not get overwritten and the armor display is the "circular" style.
Attachments
SapphireAndCenturionArmor.jpg
SapphireAndCenturionArmor.jpg (89.73 KiB) Viewed 208 times
Stupid code. Do what I want, not what I typed in!

Post Reply