Bertilsson Posted October 17, 2013 Author Share Posted October 17, 2013 (edited) I have not game tested alien grenades and battle-scanners, but I expect that battle-scanners will be completely normal and alien grenades will be offset same as frag grenades.I have now tested roughly 50 frag grenades, 20 alien grenades and 1 battle scanner and the expectation above holds true.Frag grenades and alien grenades are affected equally and battle scanners does not seem to be affected at all. I have also found that the default levels I selected seems to be very close to how I wanted the mod to work. Throwing a grenade at the feet of an alien is _very_ likely but not guaranteed to hurt that alien as the grenade needs to be offset by ~0.5 tiles just to become offset from the alien to begin with. Then it needs to be additionally offset by more than the radius of the explosion in order to leave that specific alien unharmed. Trying to catch more than one alien birds standing multiple tiles apart with a single grenade is however much less a guaranteed success and very much depending on how much offset the explosion is and in what direction. Usually it results in only 1 alien hit but in some cases it results in both or none of them being hit. So being greedy attempting to hit multiple aliens is a gamble that may or may not pay off. Most importantly the grenades are no longer the best precision weapon in the game. So in summary I'm very happy with this mini-mod exactly the way it is right now :smile: Edited October 17, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
Bertilsson Posted November 7, 2013 Author Share Posted November 7, 2013 (edited) Might I recommend working the soldier's aim value into the calculation?I have now revisited this idea, and I think it would make more sense to factor in distance instead of aim. Throwing precision is not necessarily relative to aiming skill, but it is very much relative to throwing distance. And as it turns out random throwing distance offset seems very simple implement. Original code simulated function Explode(Vector HitLocation, Vector HitNormal) { local Pawn kPawn; local XGUnit kUnit; local XComUnitPawnNativeBase kXComPawn; local float Dist; // End:0x18D if(!m_bShouldNotCauseDamageFlinch) { kPawn = WorldInfo.PawnList; J0x37: // End:0x18D [Loop If] if(kPawn != none) { kXComPawn = XComUnitPawnNativeBase(kPawn); // End:0x162 if(kXComPawn != none) { kUnit = XGUnit(kXComPawn.GetGameUnit()); // End:0x162 if(kUnit != none) { Dist = VSize(kUnit.Location - self.Location); // End:0x162 if((Dist > (GetSplashRadius())) && Dist < ((GetSplashRadius()) * float(2))) { kUnit.m_kConstantCombatData.m_bIsBeingFiredUpon = true; } } } kPawn = kPawn.NextPawn; // [Loop Continue] goto J0x37; } } super.Explode(HitLocation, HitNormal); //return; } New code: simulated function Explode(Vector HitLocation, Vector HitNormal) { local Pawn kPawn; local XGUnit kUnit; local XComUnitPawnNativeBase kXComPawn; local float Dist; // End:0x18D kPawn = WorldInfo.PawnList; J0x37: // End:0x18D [Loop If] if(kPawn != none) { kXComPawn = XComUnitPawnNativeBase(kPawn); // End:0x162 if(kXComPawn != none) { kUnit = XGUnit(kXComPawn.GetGameUnit()); // End:0x162 if(kUnit == m_kFiredFromUnit) { Dist = VSize(kUnit.Location - self.Location) / 5; //Dist / 5 == 20% of distance thrown } } kPawn = kPawn.NextPawn; // [Loop Continue] goto J0x37; } super.Explode(class'XGPlayer'.static.GetRandomPoint(HitLocation, Dist, 0.0), HitNormal); //Offset by thrown distance between 0% and 20% //return; } Throwing 15 tiles will result in explosion offset ranging from 0.0 to 3.0 tiles with average of 1.5 tiles.Adjusting the upper offset limit can be done via a single integer mod. I will most likely attempt implementing this tomorrow. Ideally grenadier perk would make the offset much smaller, but there is really no room for implementing that. Edit: Accidentally pasted first draft instead of final draft Edited November 7, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
Amineri Posted November 7, 2013 Share Posted November 7, 2013 The Rocket Scatter mod factors both range and aim into the scatter amount. I used a normal distribution in both range and azimuth. I then made the standard deviation of the distribution a linear function of range (twice as far means twice the scatter) as well as a function of soldier aim. This creates a reason to care about aim on Rocketeers (otherwise it didn't matter), as well as providing consequences for things like Suppression and Poison. The GetRandomPoint call is a great idea to conserve space. The original rocket scatter code only allowed for scattering in azimuth and not in range (making it impossible for the Rocketeer to hit himself -- no fun :sad: ), and so used some transforms to accomplish this. The GetRandomPoint generates a uniformly random radius and angle, which is only uniform in the polar coordinate system, and isn't uniform in terms of regular area. As an example:Get a random point in a radius of 2, no minimum radius.50% chance of falling from radius 0.0 to 1.0 -- covers area π * 1.0^2 = π area50% chance of falling from radius 1.0 to 2.0 -- covers area π * (2.0^2 - 1.0^2) = 3 * π area Per unit area the close in range is 3 times more likely than the further out range. This results in a distribution that is kind of bell-shaped, but follows a 2nd-order polynomial instead of the exponential of a normal distribution -- but close enough. The code I used to figure how scatter adjusted based on aim for rockets is: fScatter = class'XGTacticalGameCore'.default.MIN_SCATTER; fScatter /= float(Max(50, m_kUnit.GetOffense()) - 40); It uses the MIN_SCATTER config variable as a base, then divides by the unit's offense stat - 40. However, for scatter purposes aim can't fall below 50 (which is definitely possible with things like Suppression, Poison, or Red Fog).As of LW 2.12beta5 MIN_SCATTER is set to 75. For a Rookie with 65 aim this results in an average scatter of 3 (in tiles). For Rockets this is the std dev at a range of 20. The average scatter is then scaled linearly up or down based on range. I'm pretty sure the expected range for grenades would need to be shorter, but otherwise this could work. Perhaps something like: simulated function Explode(Vector HitLocation, Vector HitNormal) { local Pawn kPawn; local XGUnit kUnit; local XComUnitPawnNativeBase kXComPawn; local float Dist; if(!m_bShouldNotCauseDamageFlinch) { kPawn = WorldInfo.PawnList; J0x37: if(kPawn != none) { kXComPawn = XComUnitPawnNativeBase(kPawn); if(kXComPawn != none) { kUnit = XGUnit(kXComPawn.GetGameUnit()); // End:0x162 if(kUnit == m_kFiredFromUnit) { fScatter = 64.0 * class'XGTacticalGameCore'.default.MIN_SCATTER; // config scatter, Unreal units (MIN_SCATTER in config distance units) fScatter /= float(Max(50, m_kUnit.GetOffense()) - 40); // aim adjustment in Unreal units Dist = VSize(kUnit.Location - self.Location); // compute distance, Unreal units fScatter *= (Dist/(64.0*15.0)); // scale scatter based around range 15.0 } } kPawn = kPawn.NextPawn; // [Loop Continue] goto J0x37; } } super.Explode(class'XGPlayer'.static.GetRandomPoint(HitLocation, Dist), HitNormal); //Offset by thrown distance based on aim and range //return; } The m_bShouldNotCauseDamageFlinch thing prevents the randomization from happening for Smoke Grenades and Battlescanners. This is a similar formula as for rockets but for grenades it is the maximum amount of scatter, whereas for rockets it is the average amount of scatter (in both range and cross-range). For a Rookie (65 aim) throwing a frag grenade at max range would have maximum scatter of 3 distance = 2.0 tiles. 50% of the time the explosion would fall within 1.0 tiles. Frag grenades have a radius of 3.75 = 2.5 tiles, so it would always hit the aimed at target. However it would not be perfectly accurate in terms of placement. For a Heavy Floater (70 offense in LW) at Bombard range of 24 distance (16 tiles), the base scatter would be (75/(70-40)) = 2.5 at 15 tiles, which would be 2.5 * (24/15) = 4 distance = 2.67 tiles near max range. This yields a 2.5/2.67 = 93% chance to hit the aimed at target. If the Heavy Floater were Suppressed or Poisoned (reducing its effective aim to 50) the base scatter would become (75/(50-40)) = 7.5 at 15 tiles, which would be 7.5 * (24/15) = 12 distance = 8 tiles, giving a 2.5 / 8 = 31% chance to hit. This would require some AI tweaks to the grenade priority based on aim/distance etc or the aliens would tend to waste their grenades if Suppressed and at long range. Link to comment Share on other sites More sharing options...
Bertilsson Posted November 7, 2013 Author Share Posted November 7, 2013 (edited) I have now read the previous post 3 times and I'm still not sure I understand all the finer points (or possibly even the major ones) :smile: Firstly I don't see/understand the issue with area vs get random point.I assume that the direction resolution isn't unlimited which I expect results in "blind spots" that cannot be _the_ random target as distance increases. In an extreme case of low resolution with only 4 possible angles, the possible targets could be represented as an X. In this situation no single pixel would be more likely or less likely to be _the_ random target. But there would be a much higher probability for the center pixel to be affected by the explosion area. If the explosion radius is target pixel + ~1 pixel then there are 5 target pixels that would affect the center pixel but there are only 2 target pixels that each affect any given corner pixel since the outer pixels are also depending on direction and not only distance and radius as the center pixel. If scaling up the example to a real world 400 yard golf course (with infinite resolution). The further away from the hole the smaller the risk to get a golf ball in the head. Not only would the golfer need to be off the target by almost exactly the same distance to hit me in the head, but he/she also needs to be off in almost exactly the same direction. If the balls were replaced with grenades from a grenade launcher, magically being offset into a perfectly random angle and offset in a limited but perfectly and independently random distance... Where would I want to stand, if forced to pick a location inside the possible random radius? From my understanding the only variable making me safer would be distance from intended target as I cannot predict the random offset direction. So to get to the point that I am not getting... Is the idea to make to make it more likely to be hit far away from the intended target? Basically favoring longer offset distances? Similar to standing on a rice field in a Vietnam war movie being attacked by grenade launchers where no single spot is safer than any other spot in the field? I'm not trying to create a debate... I'm simply not understanding the point as it seems perfectly logical to me that random angle and random % distance makes it more important for the thrower to aim more precise as distance increase to improve the chance to actually hit the intended target and harder and harder to plan the collateral damage. The other thing I'm not really getting is whether or not .GetOffence() returns aim modified by factors such as suppressed or just the basic stat value? If it returns modified value then I think it is a great thing to factor in, even if scope really shouldn't be an advantage (which is very much not an issue given that the grenade was probably chosen instead of scope in the first place:) ) If not making the offsets too extreme, I don't think there is any real need to modify the AI... The goal I am looking for is that aiming for a cluster of enemies will rarely result in hitting all the intended targets, but still most (but not all) of the times affect the target actually aimed at. The human can of course compensate for this by not being greedy trying to maximize the number of enemies affected by the explosion, and not throwing grenades long distance while being surpressed. But the aliens on the other hand will still probably hit more than enough cover and XCom soldiers to not literally be wasting grenades. Especially if throwing more than one grenade and having better grenades from start. (Being allowed to stand up and throw grenades max distance while being surpressed is nonsense to begin with so the alien should be happy that he is allowed to throw at all, since I think I would really like to mod in a divine hand from Monty Python to kill him with in that situation.) Also it is often in human nature to be greedy and hope that luck will fix things in desperate situations, so the difference between human and AI grenade tactics may end up very similar after all... I only have one soldier left to move and those two aliens could just possibly be hit with a single grenade... But then I risk missing both... I'm going for it :smile: Edit: Being fairly accurate at short/medium distance with grenades even while being surpressed is perfectly fine. Edited November 7, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
Amineri Posted November 7, 2013 Share Posted November 7, 2013 Regarding the probability distribution thing ... it's extremely math-y, so no need to worry. I went off an a bit of tangent there :) The other thing I'm not really getting is whether or not .GetOffence() returns aim modified by factors such as suppressed or just the basic stat value? If it returns modified value then I think it is a great thing to factor in, even if scope really shouldn't be an advantage (which is very much not an issue given that the grenade was probably chosen instead of scope in the first place:) ) GetOffense() returns m_aCurrentStats[1], which includes modifiers directly affecting the unit. It doesn't include situational modifiers between a unit and a target. So, Suppression, Poison, and Red Fog decrease it, but target's cover doesn't. SCOPE, LPR, and Combat Drugs increase it, but height advantage doesn't. -------------- The numbers of course can be tweaked. In particular the "standard" range divisor can be tweaked to make grenades more/less accurate than rockets as desired. I understand your desire for grenade scatter, but others might want it to work a bit differently, so I figured I'd mention that higher scatter would require some AI tweaks. Not to mention that in EW there are some new grenade types like Needle, Poison, and Stealth. I'd imagine Stealth would have the bNoFlitch set false so it wouln't scatter but Needle and Poison could, which could make using the Needle grenade in particular a bit trickier. Link to comment Share on other sites More sharing options...
johnnylump Posted November 8, 2013 Share Posted November 8, 2013 If we don't want to overvalue aim, you COULD tie grenade accuracy to hit points, as a proxy for strength. Just a wild idea. (There's actually a residual strength variable that shows up here and there, but it would be too much trouble to implement) Link to comment Share on other sites More sharing options...
Bertilsson Posted November 8, 2013 Author Share Posted November 8, 2013 (edited) If we don't want to overvalue aim, you COULD tie grenade accuracy to hit points, as a proxy for strength. Just a wild idea. (There's actually a residual strength variable that shows up here and there, but it would be too much trouble to implement)Intuitively HP feels like a very good alternative, but it would most likely give a very large advantage to late game aliens. Right now I have implemented this: Dist = (Abs(VSize(kUnit.Location - self.Location)) / float(100)) * float(20); OldLocation = class'XGPlayer'.static.GetRandomPoint(HitLocation, Dist, 0.0); It turns thrown distance into percent and multiplies by 20 (arbitrary selected value easy to modify).Replacing percent with kUnit.GetOffence() will exactly fit if I do it like this instead: Dist = Abs(VSize(kUnit.Location - self.Location)) / float(kUnit.GetOffence() * 20); The multiplier should also probably be lowered somewhere closer to 15 which would then be max percent throw distance offset for a soldier with aim 100. Edit: Is there any risk that an alien or human could ever end up with <=0 aim? If so I would also need to make space to add safe guard preventing division by zero... Which could be done relatively easy by abandoning Dist variable and putting the distance calculation inside GetRandomPoint parameter directly but also making it very unfriendly to read... OldLocation = class'XGPlayer'.static.GetRandomPoint(HitLocation, Abs(VSize(kUnit.Location - self.Location)) / float(Min(1, kUnit.GetOffence()) * 20), 0.0); Toolboks mod of current implementation: MOD_NAME=Inaccurate Grenades AUTHOR=Bertilsson with invaluable assistance from amineri DESCRIPTION=Frag grenade and alien grenade explosions are randomly offset from target making grenades no longer a precision weapon. Version: 2.0 Compatible with XCOM Enemy Unknown versions: - Patch 4 ( Changelist: 356266 ) UPK_FILE=XComGame.upk OFFSET=7348415 [MODDED_HEX] CD 6B 00 00 50 55 00 00 00 00 00 00 B5 6B 00 00 FB 6A 00 00 00 00 00 00 BB 6B 00 00 00 00 00 00 84 00 00 00 A1 10 00 00 95 01 00 00 20 01 00 00 07 76 01 81 2D 01 A4 6B 00 00 16 0F 00 B9 6B 00 00 19 01 E9 F9 FF FF 09 00 92 F9 FF FF 00 01 92 F9 FF FF 07 76 01 77 00 B9 6B 00 00 2A 16 0F 00 B7 6B 00 00 2E CD 35 00 00 00 B9 6B 00 00 07 4B 01 77 00 B7 6B 00 00 2A 16 0F 00 B8 6B 00 00 2E 44 B8 00 00 19 00 B7 6B 00 00 0A 00 0A 35 00 00 00 1B 84 32 00 00 00 00 00 00 16 07 4B 01 72 00 B8 6B 00 00 01 3B 6A 00 00 16 0F 00 B6 6B 00 00 AB AC BA E1 D8 19 00 B8 6B 00 00 09 00 DE F8 FF FF 00 01 DE F8 FF FF 19 17 09 00 DE F8 FF FF 00 01 DE F8 FF FF 16 16 16 38 3F 2C 64 16 38 3F 2C 14 16 0F 00 BE 6B 00 00 12 20 60 95 00 00 1C 00 0A 95 00 00 00 1B 4D 34 00 00 00 00 00 00 00 BB 6B 00 00 00 B6 6B 00 00 1E 00 00 00 00 4A 16 0F 00 B9 6B 00 00 19 00 B9 6B 00 00 09 00 BD F9 FF FF 00 01 BD F9 FF FF 06 37 00 1C FB 6A 00 00 00 BE 6B 00 00 00 BA 6B 00 00 16 04 0B 53 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 0B 00 00 00 02 01 02 00 4D 2B 00 00 00 00 00 00 UPK_FILE=XComGame.upk OFFSET=7348639 [MODDED_HEX] {Set maximum random offset in hexadecimal percent of distance thrown below} 14 Edit again: Aim values lower than the multiplier would result in risk of greater offset than throw distance... So I should probably define a reasonable max offset as well. Edited November 8, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
kara42 Posted November 9, 2013 Share Posted November 9, 2013 The max aim penalty from debuffs is -75% (unless I am missing something): suppression: -30%poison: -20%mindfray: -25% So that combination could theoretically very well reduce aim to zero on some soldiers. If that is likely enough to happen to justify a division by zero safeguard I dont know. Link to comment Share on other sites More sharing options...
Bertilsson Posted November 9, 2013 Author Share Posted November 9, 2013 (edited) This is most likely the final version (before Enemy Within arrives in a few days...):OffenseValue clamped between 20 and 80Max Random offset = ThrowDistance / OffenseValue * 10 Resulting in worst case throw distance offset at 12.5% for anyone with offense at 80+And worst case throw distance offset at 50% for anyone with offense at 20 or less. A Rookie with 65 offense will be off target by 15.3% of throw distance in worst case.Same rookie would be off target by up to 28.6% throw distance while being suppressed. Object code: simulated function Explode(Vector HitLocation, Vector HitNormal) { local Pawn kPawn; local XGUnit kUnit; local XComUnitPawnNativeBase kXComPawn; local float Dist; // End:0x1A4 if(!m_bShouldNotCauseDamageFlinch) { kPawn = WorldInfo.PawnList; J0x37: // End:0x1A4 [Loop If] if(kPawn != none) { kXComPawn = XComUnitPawnNativeBase(kPawn); // End:0x179 if(kXComPawn != none) { kUnit = XGUnit(kXComPawn.GetGameUnit()); // End:0x179 if(kUnit == m_kFiredFromUnit) { Dist = (Abs(VSize(kUnit.Location - self.Location)) / float(Min(20, Max(80, kUnit.GetOffense())))) * float(7); super.Explode(class'XGPlayer'.static.GetRandomPoint(HitLocation, Dist, 0.0), HitNormal); } } kPawn = kPawn.NextPawn; // [Loop Continue] goto J0x37; } } //return; } Toolboks mod: MOD_NAME=Inaccurate Grenades AUTHOR=Bertilsson with invaluable assistance from amineri DESCRIPTION=Frag grenade and alien grenade explosions are randomly offset from target based on throw distance and offensive aim value. Version: 2.2 Compatible with XCOM Enemy Unknown versions: - Patch 4 ( Changelist: 356266 ) UPK_FILE=XComGame.upk OFFSET=7348415 [MODDED_HEX] {Replace XComProjectile_FragGrenade.Explode} CD 6B 00 00 50 55 00 00 00 00 00 00 B5 6B 00 00 FB 6A 00 00 00 00 00 00 BB 6B 00 00 00 00 00 00 84 00 00 00 A1 10 00 00 A7 01 00 00 20 01 00 00 07 A4 01 81 2D 01 A4 6B 00 00 16 0F 00 B9 6B 00 00 19 01 E9 F9 FF FF 09 00 92 F9 FF FF 00 01 92 F9 FF FF 07 A4 01 77 00 B9 6B 00 00 2A 16 0F 00 B7 6B 00 00 2E CD 35 00 00 00 B9 6B 00 00 07 79 01 77 00 B7 6B 00 00 2A 16 0F 00 B8 6B 00 00 2E 44 B8 00 00 19 00 B7 6B 00 00 0A 00 0A 35 00 00 00 1B 84 32 00 00 00 00 00 00 16 07 79 01 72 00 B8 6B 00 00 01 3B 6A 00 00 16 0F 00 B6 6B 00 00 AB AC BA E1 D8 19 00 B8 6B 00 00 09 00 DE F8 FF FF 00 01 DE F8 FF FF 19 17 09 00 DE F8 FF FF 00 01 DE F8 FF FF 16 16 16 38 3F F9 2C 14 FA 2C 50 19 00 B8 6B 00 00 0A 00 16 B3 00 00 00 1B BE 33 00 00 00 00 00 00 16 16 16 16 38 3F 2C 0A 16 1C FB 6A 00 00 12 20 60 95 00 00 1C 00 0A 95 00 00 00 1B 4D 34 00 00 00 00 00 00 00 BB 6B 00 00 00 B6 6B 00 00 1E 00 00 00 00 4A 16 00 BA 6B 00 00 16 0F 00 B9 6B 00 00 19 00 B9 6B 00 00 09 00 BD F9 FF FF 00 01 BD F9 FF FF 06 37 00 04 0B 53 0B 00 00 00 02 01 02 00 4D 2B 00 00 00 00 00 00 UPK_FILE=XComGame.upk OFFSET=7348635 [MODDED_HEX] {Lowest possible offensive value, default 14(20)} 14 UPK_FILE=XComGame.upk OFFSET=7348638 [MODDED_HEX] {Highest possible offensive value, default 50(80)} 50 UPK_FILE=XComGame.upk OFFSET=7348668 [MODDED_HEX] {Increase or decrease this value to increase or decrease max possible offset from target, default 07} 07 Edit: Updated the toolboks mod with separated key parameters and lowered multiplier to 7 instead of 10 to make offset smaller. Edited November 10, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
Amineri Posted November 9, 2013 Share Posted November 9, 2013 The max aim penalty from debuffs is -75% (unless I am missing something): suppression: -30%poison: -20%mindfray: -25% So that combination could theoretically very well reduce aim to zero on some soldiers. If that is likely enough to happen to justify a division by zero safeguard I dont know. Don't forget the Red Fog penalty. (I'd forgotten about Mindfray myself!). Red Fog applies a -15 aim penalty if wounded but over 50% HP and a -30 aim penalty if under 50% HP. So that brings the maximum possible penalty to -105%:suppression: -30%poison: -20%mindfray: -25%red fog: -30%If Red Fog is enabled the Mindfray+ red fog combo wouldn't be too uncommon, which would be a -55% penalty. Link to comment Share on other sites More sharing options...
Recommended Posts