Amineri Posted March 11, 2013 Share Posted March 11, 2013 (edited) This mod applies a constant damage reduction to all units of the "tank" type : SHIVs (all three varieties), Drones, Cyberdiscs and Sectopods. By damage reduction, I mean a fixed amount subtracted off any incoming damage. A shot that would normally hit for 3 hits for 1, or something similar. Unfortunately, there is no pop-up ability or anything fun like that in-game to indicate that they possess this ability -- they just take less damage then they otherwise would. Damage displayed in-game is consistent with damage applied, however. In terms of interaction with the HEAT ammo perk, the damage is doubled, then damage reduction is applied. For example, 6 damage doubled to 12, then reduced by 2 to 10 final damage. Interaction with Shredder Rocket is opposite -- damage reduction is applied first, then +33% damage from shredder affect is applied. For example, 8 damage applied, reduced by 2 to 6, then Shredder +33% for 8 final damage. It's a rather large chunk of code, but there are a few options that people can play with, which I will try to point out. 1) Damage reduction defaults to the formula : iDamage -= (GetUnitMaxHP +6) / 10;Division is integer, so any fractional parts are truncated. 2) The 6 can be changed by replacing the 2C 06 hex pair with 2C (your number in hex) in the second half 3) The 10 can be changed by replacing he 2C 0A hex pair with 2C (your number in hex) in the second half 4) GetUnitMaxHP can be changed to GetUnitHP by replacing the hex codes 1B 32 35 with 1B 2F 35This would cause the damage reduction to degrade as the tank's health goes down Keep in mind that any of these damage mitigation changes apply equally to human and alien tanks. Finally, I didn't check for underflow (if the values are changed so that ((GetUnitMaxHP + x) / y) can yield a negative number) -- in this case tank units would take extra damage. Enjoy! Note : Verified by witnessing a SHIV take 1 damage from a Sectoid. Any additional feedback is welcome! Known / Possible Issues:1) Underflow to delta_iDamage if HP value is low, with certain x and y parameters. May result in damage reduction being negative, and attacks will do additional damage. Fixed: Damage Reduction is now clamped. Default is zero minimum DR, but can be changed to something else. 2) Underflow to iDamage, if HP value is very high, on low damage attacks. May result in total damage being negative, resulting in attacks healing the target. For example, 60 hit point sectopod would have 6 points of damage reduction -- if hit with a plasma pistol for 3 damage, net result would be iDamage = -3. Fixed: Attacks now do minimum damage after DR. Default is zero minimum damage, but it can be changed to something else Verified : Attacks can do negative damage (negative damage numbers pop up correctly), which can "heal" units. Units healed in this fashion can go above their starting "max HP". This likely means that healing is not done using this method. 3) Interaction with healing effects. Drone healing beam on Sectopods and Cyberdiscs, ARC Thrower upgrade on SHIVs. I'm not sure if "negative healing damage" is applied through this method. More testing is needed. As confirmed by Yzaxtol (thank you!) repairing is done with a different method, so is unaffected by the damage reduction mod. ****************************** Version History **********************Version 1.0 - Original ReleaseDR is calculated using:iDamage -= (GetUnitMaxHP +6) / 10; The 6 can be changed by replacing the 2C 06 hex pair with 2C (your number in hex) in the second halfThe 10 can be changed by replacing he 2C 0A hex pair with 2C (your number in hex) in the second halfGetUnitMaxHP can be changed to GetUnitHP by replacing the hex codes 1B 32 35 with 1B 2F 35This would cause the damage reduction to degrade as the tank's health goes down Version 1.1 - Corrected underflow issuesDR is calculated using : iDamage -= MAX((GetMaxHP() + 6) / 10, 0);iDamage = MAX(iDamage, 0); Both of the zero values are set using signed int consts (instead of hard-coded zeros), so they can be changed.To change the 0 value, replace 2C 00 16 with 2C (your value in hex) 16 in the second half First instance of 2C 00 16 corresponds with 0 in iDamage -= MAX((GetMaxHP() + 6) / 10, 0);Second instance of 2C 00 16 corresponds with 0 in iDamage = MAX(iDamage, 0); Version 1.1.1 - Fixed bug that would cause game to hang when tank took damage Version 1.1.2 - Updated for patch 5 released March 15, 2013 MOD_NAME=Tank Damage ReductionAUTHOR=amineriUPK_FILE=XComGame.upkDESCRIPTION=Gives tank units damage reduction Version: 1.1.2 Works with XCOM Enemy Unknown version: Patch 5 ( Wednesday, February 20, 2013 12:47 PM - Changelist: 356266 ) [FIND]0B 07 58 00 9B 38 3A 19 01 E9 F9 FF FF 09 00 AB FE FF FF 00 01 AB FE FF FF 38 3A 24 00 16 07 58 00 82 9A 00 98 B4 00 00 1D 40 42 0F 00 16 18 0B 00 2D 01 74 AF 00 00 16 14 2D 00 89 B4 00 00 27 07 AF 01 82 77 00 96 B4 00 00 2A 16 18 38 00 19 19 00 96 B4 00 00 0A 00 63 B4 00 00 00 1B 7B 31 00 00 00 00 00 00 16 0C 00 EA A2 00 00 00 1B B8 36 00 00 00 00 00 00 2C 5B 16 16 07 AF 01 19 19 2E FE 2C 00 00 19 12 20 4F FE FF FF 0A 00 D8 F9 FF FF 00 1C F6 FB FF FF 16 09 00 98 F9 FF FF 00 01 98 F9 FF FF 09 00 F0 2C 00 00 00 01 F0 2C 00 00 3E 00 97 0F 00 00 00 1B 0D 10 00 00 00 00 00 00 35 D3 0D 00 00 D5 0D 00 00 00 00 19 1B 7B 31 00 00 00 00 00 00 16 09 00 C3 A2 00 00 00 01 C3 A2 00 00 2C 03 16 9F 00 98 B4 00 00 38 3F 19 19 2E FE 2C 00 00 19 12 20 4F FE FF FF 0A 00 D8 F9 FF FF 00 1C F6 FB FF FF 16 09 00 98 F9 FF FF 00 01 98 F9 FF FF 09 00 F0 2C 00 00 00 01 F0 2C 00 00 02 00 F0 2C 00 00 00 2C 02 16 [REPLACE]07 AF 01 19 19 2E FE 2C 00 00 19 12 20 4F FE FF FF 0A 00 D8 F9 FF FF 00 1C F6 FB FF FF 16 09 00 98 F9 FF FF 00 01 98 F9 FF FF 09 00 F0 2C 00 00 00 01 F0 2C 00 00 3E 00 97 0F 00 00 00 1B 0D 10 00 00 00 00 00 00 35 D3 0D 00 00 D5 0D 00 00 00 00 19 1B 7B 31 00 00 00 00 00 00 16 09 00 C3 A2 00 00 00 01 C3 A2 00 00 2C 03 16 07 57 01 82 77 00 96 B4 00 00 2A 16 18 38 00 19 19 00 96 B4 00 00 0A 00 63 B4 00 00 00 1B 7B 31 00 00 00 00 00 00 16 0C 00 EA A2 00 00 00 1B B8 36 00 00 00 00 00 00 2C 5B 16 16 9F 00 98 B4 00 00 38 3F 19 19 2E FE 2C 00 00 19 12 20 4F FE FF FF 0A 00 D8 F9 FF FF 00 1C F6 FB FF FF 16 09 00 98 F9 FF FF 00 01 98 F9 FF FF 09 00 F0 2C 00 00 00 01 F0 2C 00 00 02 00 F0 2C 00 00 00 2C 02 16 A2 00 98 B4 00 00 FA 91 92 1B 32 35 00 00 00 00 00 00 16 2C 06 16 2C 0A 16 2C 00 16 16 0F 00 98 B4 00 00 FA 00 98 B4 00 00 2C 00 16 00 98 B4 00 00 00 98 B4 00 00 00 98 B4 00 00 0B 0B 0B 0B 0B Edited March 15, 2013 by Amineri Link to comment Share on other sites More sharing options...
anUser Posted March 11, 2013 Share Posted March 11, 2013 wow, this is great! Sectopods starting with 30 HP and having damage mitigation = GetUnitHP() + 0 / 5 (for 6 damage prevention at full health) instead of +30% defense bonus? I like it :) I may even try to given them negative defense bonus just to see how bullets bounce off more often! btw, judging from all this 0b nulls by the end, I'd say you've changed something more than just those values. Would you mind telling what class and function is it, for those who haven't the latest patch installed? Link to comment Share on other sites More sharing options...
Yzaxtol Posted March 11, 2013 Share Posted March 11, 2013 How well does this code work with a 50Hp Sectopod? a 25 HP Cyberdisc? Link to comment Share on other sites More sharing options...
Amineri Posted March 11, 2013 Author Share Posted March 11, 2013 wow, this is great! Sectopods starting with 30 HP and having damage mitigation = GetUnitHP() + 0 / 5 (for 6 damage prevention at full health) instead of +30% defense bonus? I like it :smile: I may even try to given them negative defense bonus just to see how bullets bounce off more often! btw, judging from all this 0b nulls by the end, I'd say you've changed something more than just those values. Would you mind telling what class and function is it, for those who haven't the latest patch installed? Sure thing! The function is XcomGame.upk >> XGUnit >> OnTakeDamage XGUnit is the class where all unit state is stored. It keeps track of everything about each unit, from whether it's in cover, to if it has a height advantage, as well as little details like being poisoned and current health. OnTakeDamage is called when abilities are applied (Shot is a commonly applied ability), because only the unit taking damage knows what defensive abilities it has. In order to make room for the new code, I did have to remove something, it's true. The entire OnTakeDamage function is very large -- something just over 5000 bytes in hex code (file size) The original beginning of the function looked like this: if(WorldInfo.NetMode != 0){if(iDamage == 1000000 && (m_bMPForceDeathOnMassiveTakeDamage)){bMPForceDeathOnMassiveDamage = true;}}if(kDamageDealer != none && (kDamageDealer.GetCharacter().HasUpgrade(91))){if(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.CharacterHasProperty(GetCharacter().m_kChar.iType, 3)){iDamage *= float(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.2);}}if(IsShredded()) ... <snip> The first nested conditional appears to be some sort of debug / console code related to multiplayer, so that is what I removed. This part was 64 bytes (88 virtual bytes), so that freed up the bytes I needed to add in some new code. The next nested conditional first checks to see if the attacker unit has ability 91 (HEAT ammo), then checks to see if the current unit (the one under attack) is of type 3 (tank/robotic). It's logically an "and" condition, so I reversed the order of the conditional (first checking to see if current unit is a tank, then checking if attacker has HEAT ammo ability). The new damage reduction code goes in after the HEAT ammo bonus (if any) is applied, but within the "self is tank" conditional. It ended up looking like :if(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.CharacterHasProperty(GetCharacter().m_kChar.iType, 3)){if((kDamageDealer != none) && kDamageDealer.GetCharacter().HasUpgrade(91)) { iDamage *= float(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.2); }iDamage -= (GetUnitMaxHP() + 6) / 10 ;} My original damage reduction code didn't use a formula ... it was basically a 3 point lookup table. The problem was that code was 55 bytes (65 virtual bytes). It was 9 physical bytes short, but it reduced the virtual size of the method by 12 bytes when I filled in with 0x0B null statements. Since the entire file was >5000 bytes, I didn't want to try and go through and fix all the later jump statements in the entire file (plus that solution would be pretty brittle when a new patch comes out). The iDamage -= (GetUnitMaxHP() + 6) / 10 ; chunk is only 25 bytes -- very tight, and is only 33 virtual bytes. I fixed up the virtual size by making five dummy calls to iDamage. The final code actually reverse compiles to look like this: if(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.CharacterHasProperty(GetCharacter().m_kChar.iType, 3)){if((kDamageDealer != none) && kDamageDealer.GetCharacter().HasUpgrade(91)){iDamage *= float(XComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.2);}iDamage -= (GetUnitMaxHP() + 6) / 10 ;iDamageiDamageiDamageiDamageiDamage} It looks funny, but is logically correct and preserves the virtual size, keeping the change local to just the beginning part of the method. This also means that there is extra room to add a MAX(iDamage, 0) chunk, in case someone is interested in making a flat space at the bottom, with steeply ramping damage reduction. I'll poke around and release an update "soon" xD . Link to comment Share on other sites More sharing options...
Amineri Posted March 11, 2013 Author Share Posted March 11, 2013 How well does this code work with a 50Hp Sectopod? a 25 HP Cyberdisc? It should work just fine, although the damage reduction (with my default values) would be a bit steep with the 50hp sectopod. The damage reduction would be (50+6)/10 = 5 point reduced off each attack. Average Heavy Plasma hit is 9, which would be reduced to 4. Any critical damage is computed prior to the OnTakeDamage method being applied, so the +50% critical damage is applied prior to any damage reduction. Hmmm, I just noticed a bug ... if the attack is weak enough, and the damage reduction high enough, the damage could go negative, which would actually HEAL the tank. This can be fixed ^_^. I'll roll it in with setting the min damage reduction to zero. Link to comment Share on other sites More sharing options...
Amineri Posted March 11, 2013 Author Share Posted March 11, 2013 Please note that I added a few potential issues with this mod to the main post (it hasn't gone through extensive testing). Any help with testing these would be greatly appreciated ^_^. Link to comment Share on other sites More sharing options...
anUser Posted March 11, 2013 Share Posted March 11, 2013 I'll try if I can get it to work and I'll let you know how it performs. Thanks for the code and explanations Link to comment Share on other sites More sharing options...
Amineri Posted March 11, 2013 Author Share Posted March 11, 2013 Found and fixed both of the underflow issues. Minimum damage and minimum DR are now in effect and can be configured. Description and updated custom mod file for ToolBoks is in the original post. Link to comment Share on other sites More sharing options...
Amineri Posted March 11, 2013 Author Share Posted March 11, 2013 As a follow up, I'm going to look into a second Damage Reduction modlet to give melee units DR. The method:native simulated function bool IsMeleeOnly(); is defined in XGUnitNativeBase, which is the parent class of XGUnit. Sooo, we can't _change_ the IsMeleeOnly() method, but we can access it from within XGUnit to determine if a unit is ... well ... melee only. If I can find another place to squeeze in another conditional, then DR for melee units will be a reality! Let me know if there is any interest in this, please :) Link to comment Share on other sites More sharing options...
anUser Posted March 11, 2013 Share Posted March 11, 2013 damage reduction for the berserkers sounds ok... but for chryssalids? we'd better turn then some smoke grenades into insecticide gas, otherwise we're screwed... but hey, I cannot think of a better use for that space left, so why not?just got it working now, but there's so many tweaks to do before I can get into proper playtesting... Link to comment Share on other sites More sharing options...
Recommended Posts