Enhancement code reference

This is a moderated forum that collects tutorials, guides, and references for creating Transcendence extensions and scripts.
Post Reply
NMS
Militia Captain
Militia Captain
Posts: 569
Joined: Tue Mar 05, 2013 8:26 am

Enhancement codes don't seem to have been well documented, so I went source diving.

Here's how they're stored:

Code: Select all

enum ItemEnhancementTypes
	{
	etNone =							0x0000,
	etBinaryEnhancement =				0x0001,
	etLoseEnhancement =					0x0002,	//	Lose enhancements

	etStrengthen =						0x0100,	//	+hp, data1 = %increase (10% increments)
	etRegenerate =						0x0200,	//	data1 = rate
	etReflect =							0x0300,	//	data2 = damage type reflected
	etRepairOnHit =						0x0400,	//	repair damage on hit, data2 = damage type of hit
	etResist =							0x0500,	//	-damage, data1 = %damage adj
	etResistEnergy =					0x0600,	//	-energy damage, data1 = %damage adj (90%, 80%, etc)
	etResistMatter =					0x0700,	//	-matter damage, data1 = %damage adj (90%, 80%, etc)
	etResistByLevel =					0x0800,	//	-damage, data1 = %damage adj, data2 = damage type
	etResistByDamage =					0x0900,	//	-damage, data1 = %damage adj, data2 = damage type
	etResistByDamage2 =					0x0a00,	//	-damage, data1 = %damage adj, data2 = damage type
	etSpecialDamage =					0x0b00,	//	Immunity to damage effects:
												//		data2 = 0: immune to radiation
												//		data2 = 1: immune to blinding
												//		... see SpecialDamageTypes
	etImmunityIonEffects =				0x0c00,	//	Immunity to ion effects (blinding, EMP, etc.)
												//		(if disadvantage, interferes with shields)
	etPhotoRegenerate =					0x0d00,	//	regen near sun
	etPhotoRecharge =					0x0e00,	//	refuel near sun
	etPowerEfficiency =					0x0f00,	//	power usage decrease, 01 = 90%/110%, 02 = 80%/120%
	etSpeedOld =						0x1000,	//	decrease cycle time
	etTurret =							0x1100,	//	weapon turret, data1 is angle
	etMultiShot =						0x1200,	//	multiple shots, data2 = count, data1 = %weakening
	etSpeed =							0x1300,	//	decrease cycle time
												//		A = adj (0-100)
												//			[if disavantage] adj by 5% starting at 100%.
												//
												//		B = min delay in ticks (do not decrease below this; 0 = no limit)
												//		C = max delay in ticks (do not increase above this; 0 = no limit)
	etConferSpecialDamage =				0x1400,	//	weapon gains special damage
												//		A = SpecialDamageTypes
												//		B = damage level
	etHPBonus =							0x1500,	//	+hp%, like etStrengthen
												//		X = %increase

	etData1Mask =						0x000f,	//	4-bits of data (generally for damage adj)
	etData2Mask =						0x00f0,	//	4-bits of data (generally for damage type)
	etTypeMask =						0x7f00,	//	Type
	etDisadvantage =					0x8000,	//	If set, this is a disadvantage

	etDataAMask =					0x000000ff,	//	8-bits of data
	etDataAMax =						   255,
	etDataBMask =					0x00ff0000,	//	8-bits of data
	etDataBMax =						   255,
	etDataCMask =					0xff000000,	//	8-bits of data
	etDataCMax =						   255,
	etDataXMask =					0xffff0000,	//	16-bits of data
	etDataXMax =						 65535,
	};
What this means:

Enhancements are stored as a DWORD, 8 hexadecimal digits (0-9, A-F), like this: 0x00000000. However, often only the last (rightmost) 4 digits will be shown, like this: 0x0000. (In this case the rest are 0.)

The type of the enhancement is stored here: 0x00000000 or 0x0000. If the left one of those two digits is 8 or greater, the enhancement has a negative effect, and you subtract 8 from that digit before figuring out the enhancement type. The enhancement types are listed in the first part of the code above.

The other digits store data about the enhancement. The first four digits are DataX, or DataC followed by Data B (two digits each). The last two digits are DataA or Data2 followed by Data1 (one digit each).

Some enhancement types require searching the code to figure out exactly how they work.
- The ones that use Data1 to store a numerical value tend to go in 10% increments.
- In the case of etSpeedOld, DataA is the adjustment. If it's a positive enhancement, the cycle time is reduced by 10% * DataA. So 0x1002 (applied by weapon enhancement ROM) is 80% cycle time, or 25% faster fire/activation rate. If DataA is not in the range of 0 to 9, it's treated as 9 (10x fire rate). If it's a negative enhancement, it has the reciprocal effect, so 0x9001 would make a device 90% as fast.

Here's the code that determines what happens when you apply an enhancement. It's complicated, but there's some useful information. For instance, an enhancement can be replaced by an enhancement of the same type if the new one is better or affects a different damage type. A positive enhancement can remove a negative enhancement of the same type, or the damaged status. And a regenerating enhancement can remove any negative enhancement.

Code: Select all

EnhanceItemStatus CItemEnhancement::Combine (const CItem &Item, CItemEnhancement Enhancement)

//	Combine
//
//	Combine the current enhancement with the given one

	{
	DWORD dwNewMods = Enhancement.m_dwMods;

	//	If we're losing the enhancement, then clear it

	if (dwNewMods == etLoseEnhancement)
		{
		if (IsEnhancement())
			{
			*this = CItemEnhancement();
			return eisEnhancementRemoved;
			}
		else
			return eisNoEffect;
		}

	//	If the item is not currently enhanced, then we take the new enhancement

	else if (m_dwMods == etNone)
		{
		//	For strength/hpBonus, use the following rules:
		//
		//	etStrengthen 0 -> Min(10%, maxHPBonus)
		//	etStrengthen {level} -> Min( {level} * 10, maxHPBonus )

		if (Enhancement.GetType() == etStrengthen
					|| Enhancement.GetType() == etHPBonus)
			{
			int iMaxBonus = Item.GetType()->GetMaxHPBonus();
			int iNewBonus = Min((Enhancement.IsStacking() ? 10 : Enhancement.GetHPBonus()), iMaxBonus);
			if (iNewBonus > 0)
				{
				SetModBonus(iNewBonus);
				return eisOK;
				}
			else
				return eisNoEffect;
			}

		//	For all others, take the enhancement

		else
			*this = Enhancement;

		return eisOK;
		}

	//	If already enhanced

	else if (m_dwMods == dwNewMods)
		{
		if (IsDisadvantage())
			return eisNoEffect;
		else
			return eisAlreadyEnhanced;
		}

	//	We currently have a disadvantage

	else if (IsDisadvantage())
		{
		//	We have a disadvantage and the enhancement is another
		//	disadvantage.

		if (Enhancement.IsDisadvantage())
			{
			switch (Enhancement.GetType())
				{
				//	If we're making the disadvantage worse, then
				//	continue; otherwise, no effect.

				case etRegenerate:
				case etResist:
				case etResistEnergy:
				case etResistMatter:
				case etPowerEfficiency:
					{
					if (Enhancement.GetType() == GetType()
							&& Enhancement.GetLevel() > GetLevel())
						{
						*this = Enhancement;
						return eisWorse;
						}
					else
						return eisNoEffect;
					}

				case etHPBonus:
				case etStrengthen:
					{
					if ((GetType() == etHPBonus || GetType() == etStrengthen)
							&& Enhancement.GetHPBonus() < GetHPBonus())
						{
						*this = Enhancement;
						return eisWorse;
						}
					else
						return eisNoEffect;
					}

				case etSpeed:
				case etSpeedOld:
					{
					if ((GetType() == etSpeed || GetType() == etSpeedOld)
							&& Enhancement.GetActivateRateAdj() > GetActivateRateAdj())
						{
						*this = Enhancement;
						return eisWorse;
						}
					else
						return eisNoEffect;
					}

				case etResistByLevel:
				case etResistByDamage:
				case etResistByDamage2:
					{
					if (Enhancement.GetType() == GetType()
							&& Enhancement.GetDamageType() == GetDamageType()
							&& Enhancement.GetLevel() > GetLevel())
						{
						*this = Enhancement;
						return eisWorse;
						}
					else
						return eisNoEffect;
					}

				//	Otherwise, a disadvantage does not affect a disadvantage

				default:
					return eisNoEffect;
				}
			}

		//	We have a disadvantage and we use an enhancement.

		else
			{
			switch (Enhancement.GetType())
				{
				//	Regeneration enhancement always repairs a disadvantage

				case etRegenerate:
					{
					*this = CItemEnhancement();
					return eisRepaired;
					}

				//	If the enhancement is the opposite of the disadvantage
				//	then the disadvantage is repaired.

				case etResist:
				case etResistEnergy:
				case etResistMatter:
				case etPowerEfficiency:
				case etResistByLevel:
				case etResistByDamage:
				case etResistByDamage2:
				case etReflect:
					{
					if (GetType() == Enhancement.GetType())
						{
						*this = CItemEnhancement();
						return eisRepaired;
						}
					else
						return eisNoEffect;
					}

				case etHPBonus:
				case etStrengthen:
					{
					if (GetType() == etHPBonus || GetType() == etStrengthen)
						{
						*this = CItemEnhancement();
						return eisRepaired;
						}
					else
						return eisNoEffect;
					}

				case etSpeed:
				case etSpeedOld:
					{
					if (GetType() == etSpeed || GetType() == etSpeedOld)
						{
						*this = CItemEnhancement();
						return eisRepaired;
						}
					else
						return eisNoEffect;
					}

				//	Otherwise, no effect

				default:
					return eisNoEffect;
				}
			}
		}

	//	We currently have an enhancement

	else
		{
		if (!Enhancement.IsDisadvantage())
			{
			switch (Enhancement.GetType())
				{
				case etHPBonus:
				case etStrengthen:
					{
					if (GetType() != etHPBonus 
							&& GetType() != etStrengthen)
						return eisNoEffect;

					//	If improving...

					int iOldBonus = GetHPBonus();
					int iMaxBonus = Item.GetType()->GetMaxHPBonus();
					int iNewBonus = Min((Enhancement.IsStacking() ? iOldBonus + 10 : Enhancement.GetHPBonus()), iMaxBonus);
					if (iNewBonus > iOldBonus)
						{
						SetModBonus(iNewBonus);
						return eisBetter;
						}
					else
						return eisNoEffect;
					}

				//	If this is the same type of enhancement and it is better,
				//	then take it (otherwise, no effect)

				case etRegenerate:
				case etResist:
				case etResistEnergy:
				case etResistMatter:
				case etPowerEfficiency:
					{
					if (Enhancement.GetType() == GetType()
							&& Enhancement.GetLevel() > GetLevel())
						{
						*this = Enhancement;
						return eisBetter;
						}
					else
						return eisNoEffect;
					}

				case etSpeed:
				case etSpeedOld:
					{
					if (Enhancement.GetType() == GetType()
							&& Enhancement.GetActivateRateAdj() < GetActivateRateAdj())
						{
						*this = Enhancement;
						return eisBetter;
						}
					else
						return eisNoEffect;
					}

				case etResistByLevel:
				case etResistByDamage:
				case etResistByDamage2:
					{
					if (Enhancement.GetType() != GetType())
						return eisNoEffect;
					else if (Enhancement.GetDamageType() != GetDamageType())
						{
						*this = Enhancement;
						return eisEnhancementReplaced;
						}
					else if (Enhancement.GetLevel() > GetLevel())
						{
						*this = Enhancement;
						return eisBetter;
						}
					else
						return eisNoEffect;
					}

				default:
					return eisNoEffect;
				}
			}
		else
			{
			//	A disadvantage always destroys any enhancement

			*this = CItemEnhancement();
			return eisEnhancementRemoved;
			}
		}
	}
RPC
Fleet Admiral
Fleet Admiral
Posts: 2876
Joined: Thu Feb 03, 2011 5:21 am
Location: Hmm... I'm confused. Anybody have a starmap to the Core?

We had a list of enhancements back on the legend page of the wiki. I guess it's time for me to update it ;)

A couple questions:
What is ItemEnhancementTypes?
How are enhancements done in code now?

Sorry for not making the edit on my own right now but I think Trans still has some backwards compatibility so I want to make the edit when I actually know what I'm talking about.
Tutorial List on the Wiki and Installing Mods
Get on Discord for mod help and general chat
Image
Image
Der Tod ist der zeitlose Frieden und das leben ist der Krieg
Wir müssen wissen — wir werden wissen!
I don't want any sort of copyright on my Transcendence mods. Feel free to take/modify whatever you want.
NMS
Militia Captain
Militia Captain
Posts: 569
Joined: Tue Mar 05, 2013 8:26 am

Yeah, I knew about that page, but it was not as detailed as I would have liked. Even the documentation in the code comments is less than complete.

ItemEnhancementTypes is an enum, which is basically a way to assign names to (numerical) variable values. So when you see

Code: Select all

if (m_dwMods == etNone)
it's actually checking whether the variable m_dwMods is equal to 0, but it's easier to understand what the code does. This is more efficient and less prone to bugs caused by typos than having the variables be strings and checking for them to match a specific string. This is only relevant in the C code.

If you're asking how they work behind the scenes (in C), a lot of the relevant code, like what I quoted, is in Mammoth/TSE/CItemEnhancement.cpp. If you're asking for details on how to use them in TLisp or XML, maybe I'll get back to you on that, if I decide to figure out how CodeChain works. But it should be possible to follow examples in the existing code, such as:

Code: Select all

<Device chance="30" item="&itOmniTeV9Blaster;" enhancement="0x1005"/>
which gives the Heavy IAV a 30% chance to have an omni TeV-9 with double fire rate, and

Code: Select all

(objEnumItems gSource "aI" theItem
						(shpEnhanceItem gSource theItem 0x0808)
						)
which gives Arco Vaughn's armor 80% laser and kinetic resist.
Post Reply