Bertilsson Posted October 15, 2013 Share Posted October 15, 2013 (edited) I'm currently working on a little mini-mod that will:Offset the explosion of grenades in a random direction.Remove the effect that makes soldiers jump into the air if within double distance of the explosion-radius of a grenade, to make space for the mod.The reason for the mod is that I think grenades with perfect accuracy is nothing short of silly and makes them a really unfun element in the game, especially in the hands of mutons, but also in the hands of my own soldiers. There should always be risks involved when throwing a grenade and not be possible to predict with absolute precision exactly who or what is going be damaged or completely untouched in a crowd. I expect to complete the mini-mod tomorrow and would ideally like to hear input from someone else what is reasonable offsets and probabilities. Probability to not make a perfect hit: 75%?Distance from intended target to actual explosion when not perfect: 1 Tile? More? Less? Edit: If someone could help me figure out how to call XGPlayer.GetRandomPoint() from inside XComProjectile_FragGrenade.Explode() function then it would be very simple to create very specific rules and to make them relative to the size of the explosion. Example: 90% chance that the explosion will be offset by between 25% and 75% of the explosion radius and 10% chance that it will be offset by 75% to 150% of the explosion radius making it possible to completely miss the target. Edit 2toolboks custom mod for latest version: 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: 0.9 (Probably needs some re-balancing regarding offsets and probabilites) 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 3D 01 00 00 20 01 00 00 07 4B 00 96 A7 2C F9 16 2C 01 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 1E 00 00 F0 43 1E 00 00 90 43 4A 16 06 1E 01 07 96 00 96 A7 2C 63 16 2C 05 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 1E 00 00 90 43 1E 00 00 40 43 4A 16 06 1E 01 07 E1 00 96 A7 2C 63 16 2C 3C 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 1E 00 00 40 43 1E 00 00 C0 42 4A 16 06 1E 01 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 1E 00 00 C0 42 1E 00 00 00 00 4A 16 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 0B 0B 0B 0B 0B 0B 0B 0B 0B 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 Edited October 18, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
dubiousintent Posted October 15, 2013 Share Posted October 15, 2013 (edited) From practical experience I suggest that deviation from the target point of impact is more likely to be short rather than long (grenades are surprisingly heavy for their size), and the fall pattern will be an oval rather than a circle. Sideways deviation is comparatively low, ranging variation is greater. Deviation greater than a tile ought to be no more than a low percentage of the range, say 10%. You are basically throwing a heavy baseball or (US) football (depending upon the shape of the particular grenade). Alien grenades might reasonable be more inaccurate (just how comparable are they?), but in a similar pattern. How practical it is to implement of course is another matter. -Dubious- Edited October 15, 2013 by dubiousintent Link to comment Share on other sites More sharing options...
Bertilsson Posted October 15, 2013 Author Share Posted October 15, 2013 (edited) While I do agree that the offset while the grenade is airborn is likely to be exactly as you describe there is also the factor of rolling on impact before explosion. On a flat hard surface it would be reasonable to expect the grenade to roll in the direction it was thrown, while on softer ground it would stop completely on impact. Additional factors would be gravity if landing on uneven ground, rolling downwards in whatever direction that may be. Or hitting objects while rolling or airborn... And finally the explosion may be focused away from hard objects, such as walls and rocks. Not to mention goofy factors such as accidentally dropping the grenade, losing foothold or getting tangled up in the shoulder-strap of the gun in the middle of the throw :smile: But ultimately it is right now only possible to offset the explosion, from the perfectly placed location of the grenade, without any relation to direction and location of the thrower. As long as we are not talking about several meters offset and still keeping the center of the explosion inside the expected blast radius I think it would not be too immersion breaking that the explosion is offset from the actual grenade, in any direction.Especially considering that most of the time the glam cam makes it difficult to see exactly where it landed and that the user of the mod would not expect the explosion to occur exactly where it was planned to occur. Edited October 15, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
TheOldOne822 Posted October 16, 2013 Share Posted October 16, 2013 I'd like it if it could be made to have a small chance of "perfect" accuracy with a much bigger chance of being off by 1 and a very small chance of 2 and tiny chance of off 3. Some people are very good at throwing things right where they want them but as you pointed out there are many things that could mess it up on a battlefield. Link to comment Share on other sites More sharing options...
Bertilsson Posted October 16, 2013 Author Share Posted October 16, 2013 (edited) I like that idea very much.It would be easy to implement as follows:RandomVariable = RandomNumber 0-99If RandomVariable < 10 { Define the random offset area as a small circle by assigning min and max offset distance values to variables}else if RandomVariable < 20 { Define the random offset area as a donut-shaped area outside the small circle}else if RandomVariable < 30 { Define the random offset area as a donut-shaped area outside the previous donut}and so on. It would then be very simple for anyone to meta-mod into personal liking by simply modifying the "if %probability" spans and the "thicknesses" of each donut. Right now the only thing missing for that plan to work is being able to use the XGPlayer.GetRandomPoint() which is hopefully very close to happen...(Provided that Amineri is able to figure out what I did wrong and fix it after she have already told me almost exactly how to do it :ohdear: ) In worst case that GetRandomPoint-function can not be used it would have to be a much smaller number of possible donuts with fixed distances (closer to diamond shaped rings) and a lot less meta-mod friendly, but it should still be possible to implement. Edited October 16, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
johnnylump Posted October 16, 2013 Share Posted October 16, 2013 Might I recommend working the soldier's aim value into the calculation? Link to comment Share on other sites More sharing options...
Bertilsson Posted October 16, 2013 Author Share Posted October 16, 2013 (edited) That would indeed make a lot of sense...And I guess would be possible by keeping and re-purposing the loop of all soldiers originally used to make them jump, to something like this:If(self.m_kFiredFromUnit == kUnit){ Some formula defining min and max offset values based on aim of kUnit} But I'm fairly sure that I am not capable of implementing it :sad: But yes, as stated it would actually make most sense and make the donuts irrelevant. Edit: If the size available to mod wouldn't be very limited when keeping the soldier loop it would likely also have been possible to factor in the direction in which the grenade was thrown into a more elliptic offset as suggested by dubiousintent... But I imagine it will already be a very tight fit to squeeze in aim as a factor. Edited October 16, 2013 by Bertilsson Link to comment Share on other sites More sharing options...
Bertilsson Posted October 16, 2013 Author Share Posted October 16, 2013 Edit: I didn't mess up afterall... Amineris instructions were perfect to the letter and I was able to follow them even when I thought I had failed to do so :D So I guess I will implement the donuts next :) Link to comment Share on other sites More sharing options...
Bertilsson Posted October 17, 2013 Author Share Posted October 17, 2013 After some really weird and unexpected issues with grenades exploding completely offset whenever I borrowed variables to use both for probabilities and and min/max-offset I finally gave up on making a statistically correct % model and to make it mod friendly via variables and basically brute force implemented 4 alternatives. XComGame.upk --> XComProjectile_FragGrenade.Explode() Decimal offset: 7348415, Hex offset: 7020BF 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 3D 01 00 00 20 01 00 00 07 4B 00 96 A7 2C F9 16 2C 01 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 1E 00 00 F0 43 1E 00 00 90 43 4A 16 06 1E 01 07 96 00 96 A7 2C 63 16 2C 05 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 1E 00 00 90 43 1E 00 00 40 43 4A 16 06 1E 01 07 E1 00 96 A7 2C 63 16 2C 3C 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 1E 00 00 40 43 1E 00 00 C0 42 4A 16 06 1E 01 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 1E 00 00 C0 42 1E 00 00 00 00 4A 16 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 0B 0B 0B 0B 0B 0B 0B 0B 0B 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 The following offsets and probabilities have been implemented:0.4% probability for offset by 3.0 to 5.0 tilesOtherwise 5% probability for offset by 2.0 to 3.0 tilesOtherwise 60% probability for offset by 1.0 to 2.0 tilesOtherwise 100% probability for offset by 0.0 to 1.0 tiles It should roughly translate to average 0.5 tiles offset in 1/3 of cases, average 1.5 tiles offset in almost 2/3 of cases and 1/20 average offset 2.5 tiles and freak accident offset by average 4.0 tiles in 1/250. Visually in the ~95% of cases where offset is less than 2 tiles it looks like the explosion goes of sideways or on target depending on distance from grenade. In the remaining cases user will probably have to imagine that the grenade somehow ended up where the explosion occurred. Feel free to modify to liking.A tile is 96.0 wide.Offset values can be modded by searching for 1E 00 00 XX XX 1E 00 00 YY YY where X is big endian float for max offset and Y is big endian float for min offset.A good float converter can be found here:http://www.h-schmidt.net/FloatConverter/ Probabilties can be found by searching for 2C 63 (or 2C F9) followed by 2C %value(I will probably attempt to convert the float values to more mod friendly integers corresponding to 0.1 tiles when and if I get around to adjusting the values during game play) 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. Link to comment Share on other sites More sharing options...
Amineri Posted October 17, 2013 Share Posted October 17, 2013 (edited) For those that are interested in some of the details of generating "new" hex code, here is a copy of the conversation reply I sent to Bertilsson in order to be able to call XGPlayer.GetRandomPoint(...) from the XComProjectile function: So, technically there are up to three ways to access functions/variables in another class. 1) Use a local/class variable of the class as an accessor, as in : kPlayer.GetRandomPoint(), where kPlayer is a local variable in Explode(), or m_kPlayer.GetRandomPoint(), where m_kPlayer is a class variable in XComProjectile_FragGrenade (or a parent class such as XComProjectile). 2) Use a remote access convention to access the remote function via a 'replication event'. These are only enabled for certain classes (typically the XGTacticalGameCore and XGBattle), and look likeXComGameReplicationInfo(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kGameCore.IsOptionEnabled(4). 3) In certain cases a static call can be made to a class to access a function in that class. However, this call is a generic call and there will be no class variable information available in the class call. These calls look like class'XGItemLibrary'.static.GetItem(kInventory.arrLargeItems). Note that parameter information can still be passed and used. In this example the GetItem function only uses the passed large item information info (and static info defined in the XGItemLibrary class). For this particular case : 1) XGPlayer.GetRandomPoint(...) does not use access any XGPlayer class variables in order to generate said random point, so that means that option 3 is viable. 2) XGPlayer is not configured for making GRI calls, so option 2 is not possible. 3) There doesn't appear to be a simple access to a variable of type XGPlayer from XGProjectile. The best reference I found in vanilla code was : XComUnitPawn(XComWeapon(Owner).Owner).GetGameUnit().GetPlayer().IsHumanPlayer() to access the IsHumanPlayer() function from XGPlayer. This means that option 1 is possible, but the code is probably uglier than using option 3. 4) The AI/human player can be accessed through a GRI call to XGBattle, via a call such as :XGAIPlayer(XGBattle_SP(XComTacticalGRI(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kBattle).GetAIPlayer())orXGPlayer(XGBattle_SP(XComTacticalGRI(class'Engine'.static.GetCurrentWorldInfo().GRI).m_kBattle).GetHumanPlayer())However, this is being used for both alien and XCOM units presumable, which would just make the code more confusing (and longer in hex). Given the various options, it looks like option 1 is the simplest cleanest option in this case (since the GetRandomPoint(...) function doesn't actually access any XGPlayer class variables). Such a construction will look like class'XGPlayer'.static.GetRandomPoint(...) when decompiled by UE Explorer. Unfortunately there are no such static references to class'XGPlayer' in the vanilla code, so the following shows the steps I go through in order to create such a construction (which I do so very rarely). I've only messed with this part of Unreal code 1 or 2 times, so this will be a bit of discovery for me as well. I'm going to heavily use XGLoadoutMgr.ConvertTInventoryToSoldierLoadout and XGLoadoutMgr.ApplyLoadout as templates, since I'm familar with both functions (from the new items mod) and they use class constructions heavily. 1) The 0x20 token is the "class identifier token". From ConvertTInventoryToSoldierLoadout 20 7C B9 00 00 is the hex forclass'XGWeapon_Grapple'. First to figure out where to find the correct class reference within UE Explorer. The XGWeapon_Grapple reference is 0xB97C = 47484. In UE Explorer's XGWeapon_Grapple's Object Properties view, the default value is Default__XGWeapon_Grapple(47485), which is the above value +1. Also the export table value is XGWeapon_Grapple(474831). Another line has 20 04 A7 00 00 as the hex for class'XGItemLibrary'. 0xA704 = 42756. In the Object Properties view for XGItemLibrary the default value is Default__XGItemLibrary(42757), which is again the hex value +1. The export table value is XGItemLibrary(427551). Together this should allow for retrieving the reference for XGPlayer. XGPlayer's object properties view has Default__XGPlayer(38241) and export entry XGPlayer(382391), so the reference value for XGPlayer is 38240 = 0x9560. This means that the hex code for class'XGPlayer' is 20 60 95 00 00. 2) From XGLoadoutMgr.ConvertTInventoryToSoldierLoadout the construction class'XGItemLibrary'.static.GetItem(kInventory.iPistol) is represented using the following hex: 12 20 04 A7 00 00 26 00 FD A6 00 00 00 1B D1 32 00 00 00 00 00 00 35 C8 0D 00 00 CA 0D 00 00 00 00 00 D8 A8 00 00 16 This hex breaks down as follows:12 -- EX_ClassContext (this replaces the usual 0x19 token in this case)20 04 A7 00 00 -- class'XGItemLibrary'26 00 -- size of context (i.e. GetItem(kInventory.iPistol))FD A6 00 00 -- return value of context (i.e. of GetItem)00 -- an extra zero for some reason, as with the 0x19-type context constructs1B D1 32 00 00 00 00 00 00 -- GetItem( 0x1B virtual function token followed by GetItem reference35 C8 0D 00 00 CA 0D 00 00 00 00 00 D8 A8 00 00 -- struct-type construction for kInventory.iPistol (structs use 0x35 token and a different hex arrangement)16 -- end function token to end the GetItem function call (i.e. denotes the end of the parameter list) Following a similar arrangement should allow for construction of class'XGPlayer'.static.GetRandomPoint(...) However, we'll need to find the reference and return value reference for GetRandomPoint(...). The easiest place I see for this is in XComAlienPod.RandomizeLocation, with the construction : kAlien.m_kPlayer.GetRandomPoint(vSearchCenter, fSearchRadius); This has hex code : 19 19 00 E1 38 00 00 09 00 63 AF 00 00 00 01 63 AF 00 00 1E 00 0A 95 00 00 00 1B 4D 34 00 00 00 00 00 00 00 DC 38 00 00 00 D8 38 00 00 4A 4A 16 Breaking this down yields:19 -- context token19 -- context token00 E1 38 00 00 -- kAlien09 00 -- size of next context (i.e. m_kPlayer)63 AF 00 00 -- return reference next context (i.e. m_kPlayer) 00 -- extra zero01 63 AF 00 00 -- class variable m_kPlayer (note that return value reference = variable reference)1E 00 -- size of next reference (i.e. GetRandomPoint(vSerachCenter, fSearchRadius))0A 95 00 00 -- return reference next context (i.e. GetRandomPoint(...))00 -- extra zero1B 4D 34 00 00 00 00 00 00 -- virtual function GetRandomPoint( reference00 DC 38 00 00 -- local variable vSearchCenter00 D8 38 00 00 -- local variable fSearchRadius4A -- no parameter4A -- no parameter16 -- end function token So, the return reference for GetRandomPoint is 0A 95 00 00The function reference for GetRandomPoint is 4D 34 00 00These could also be found directly via UE Explorer, if needed. Now to start building the class'XGPlayer'.static.GetRandomPoint(...) hex : 12 -- EX_ClassContext token20 60 95 00 00 -- class'XGPlayer'## 00 -- size of next context (value depends on parameter list of GetRandomPoint(...))0A 95 00 00 -- return reference next context (i.e. GetRandomPoint(...))00 -- extra zero1B 4D 34 00 00 00 00 00 00 -- virtual function GetRandomPoint( reference -- center point hex -- radius hex4A -- no parameter4A -- no parameter16 -- end function token The two optional parameters in GetRandomPoint(...) are : optional float fMinRadius, optional Vector VDir. I'm not sure if you'd want to set either of these to something other than the default values. The default fMinRadius is 0.0. If no vDir is supplied then the code generates a random direction vector. Assuming these both stay as 0x4A tokens, context size will be 0x0C + (centerpoint size) + (radius size). It's not just the answer but also details the steps I go through in order to find/construct an 'original' piece of hex code. Edited October 17, 2013 by Amineri Link to comment Share on other sites More sharing options...
Recommended Posts