Time to Give Predator Speed to Enemies of Dragon’s Dogma
The strange, but beloved, game of Dragon’s Dogma is currently in the middle becoming Omnified. Once it has been Omnified, I’ll eventually debut it live on my stream where it shall become the definitive brutal playthrough. We just implemented the Apocalypse system into the game, making the hits from enemies result in unpredictable and wild damage.
Today we’re going to be implementing my Predator system, which gives enemies a big, big speed boost in an intelligent manner. Enemies don’t just zoom off the map randomly, they will hone in and dash at lightning speed to deliver absolute death to your heroic butt. One of these days I will be publishing a detailed Omnified Design article on the Predator system, but until I get around to doing that, check out the easy to follow summary video I put up on my YouTube:
The Predator system, just like the Apocalypse system, is a game-neutral system that we can hook up to any game and let it do its magic. It is part of the Omnified framework, a shared assembly library file used by my hacks.
While it works with any game I hook it up to, some modification of it was required during the Omnifying of Dragon’s Dogma. Not due to any game-specific reason, but due to the fact that Dragon’s Dogma is 32-bit. As I mentioned in the Apocalypse implementation article for Dragon’s Dogma, some behind the scenes work was required in order to get the various systems in my framework compatible with 32-bit applications.
It’s been a rather interesting “ordeal” actually, and I intend on writing a dedicated article for the problem of supporting both 32-bit and 64-bit applications with my game-neutral framework in the future. That aside, let’s get to plugging in the Predator system into Dragon’s Dogma.
Where’s the Movement Application Code?
Like all Omnified systems, finding the proper location for an initiation point is a key part of the implementation process. For the Predator system, the initiation point must be inserted into the movement application code; the place in the code where movement offsets are being applied to a place in memory that is holding the creature’s current coordinates.
Once these coordinate values have the movement offsets applied to them, they represent the creature’s new location on the map. Later on, these coordinate values are then committed to the creature’s actual, live coordinates, causing them to effectively move.
To find the movement application code, let’s first start off by finding the location update code, which is where the current coordinate values are set to their new calculated values. We’ll do this by figuring out what code is changing our player’s coordinates when we’re moving around.
Ideally we want to see only a single method writing to our coordinates, and only when we’re moving. Sometimes we get a less than ideal result, with a single method that is constantly writing to our coordinates, even when we aren’t moving.
Unfortunately for us, Dragon’s Dogma appears to have a whole army of different methods all updating our coordinates. Very unorthodox.
Not only are there many functions writing to our coordinates, the majority of them are executing constantly. The third and fifth instructions pictured above only go off while the character is in the air, and the last instruction seems to only go off when we’re stumbling downhill.
While on the outset this seems to make finding the singular “movement application” bit of code a much taller order, I refuse to believe that we need to concern ourselves with any more than one of these instructions. In order to gauge the importance of these instructions, we’re going to go through each one and remove the instruction, preventing its execution, and seeing if the character can no longer move.
It quickly becomes apparent that the first instruction, located at DDDA.exe+44D042 is the one we’re interested in. After removing it, we can no longer move on the X-axis. As you can tell from the instruction itself (movss), this instruction only updates a single floating point value. I’m more used to seeing a single instruction updating the entire set of coordinates at once (typically with a movaps), but in Dragon’s Dogma, I guess they like to do one thing at a time.
So the first instruction movss [esi+40],xmm6
updates the X coordinate alone. The Y coordinate is updated by the instruction movss [esi+44],xmm6
a few lines down. Finally, the Z coordinate is updated by the instruction movss [esi+48],xmm6
yet a few more lines down. If we remove all these lines, the character will be stuck in place.
The code we’re looking at is the core location update code. It effectuates changes to the player’s coordinates due to movement, and is responsible for the character moving across our screens. The other instructions we saw updating our coordinates may be there for validation purposes, or may handle very specific types of movement. We’ll figure that out if we have to.
That aside, we’re forgetting to address the 10,000 pound elephant in the room that’s staring right at us from this code: the movement application code is right next to the location update code! Shocking, and also horribly convenient. Never seen that before! Just looking at the code, we see an offset being loaded into xmm6 from the stack, and then that value being added to the character’s current coordinate values.
We’ll probably want to verify that movement offsets are indeed being added to the coordinates here. Since this code constantly executes, we’ll want to place a breakpoint on top of the correct instruction, and then add a condition so we only break when the player’s location structure is being modified.
Then, with a hotkey bound to Debug -> Run, we go to the game and move our character forward while spamming that hotkey. We want to get it to break midmovement. After doing this, this is what our stack looks like during execution of the movement application code:
The stack is where the movement offsets are sourced from. We see that the X offset is -11.15, the Y offset is 0, and the Z offset is 7.51. These may appear to be huge offsets, but they are correct; remember, the coordinate system in Dragon’s Dogma is very unprecise — huge coordinate values are the norm. A difference of 11 coordinate units would be barely a few pixels.
So it looks like we’ll be implementing our initiation point here. Let’s get to it.
Throwing Together the Predator Initiation Hook
The exact place we’ll be injecting our initiation point into will be a few instructions above the previously mentioned one, specifically at DDDA.exe+44D030. Let’s take a look at the starting template of our hook:
Predator Initiation Hook – Template
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Initiates the Predator system. define (omnifyPredatorHook, "DDDA.exe" + 44D030) assert (omnifyPredatorHook, F3 0F 10 74 24 30) alloc (initiatePredator,$1000, omnifyPredatorHook) registersymbol (omnifyPredatorHook) initiatePredator: initiatePredatorOriginalCode: movss xmm6 ,[ esp +30] jmp initiatePredatorReturn omnifyPredatorHook: jmp initiatePredator nop initiatePredatorReturn: |
Our mission is to call the Predator system and do what we need to do in order to ensure the movement offsets are updated to their Predator-determined values prior to the original code movss xmm6,[esp+30]
executing.
At the time of writing, I haven’t published a Predator specific article yet, so I will go over some of the basic requirements we must meet in order to call it from our code. There is a single, public facing, function we must call, and its name is executePredator. The parameters for this function are as follows:
- The player’s X, Y, and Z coordinate values.
- The moving enemy’s X, Y, and Z coordinate values.
- The moving enemy’s dimensional scales (width, height, and depth).
- The moving enemy’s X, Y, and Z movement offsets.
I’ll be demonstrating how we pass these parameters to the function, which actually requires a bit of care since we’re in 32-bit land here. More information about this will be able to be found in that Predator article whenever I get around to writing it.
The return values of the Predator function are stored in the eax, ebx, and ecx registers. These will hold the updated X, Y, and Z movement offsets.
Things to Keep in Mind…
The movement offsets are going to be sourced from, and written back to, the stack. The movement offsets for X, Y, and Z are found at [esp+30], [esp+34], and [esp+38] respectively.
Unfortunately, being on the stack, they’re subject to the stack shifting from anything we decide to push to it. So we’re going to have to do another little round of what I call “stack math”.
- We will be backing up conditional flags using pushf, this will shift everything on the stack down by 2 bytes.
- We will probably need to back up two SSE registers worth of data to the stack, namely what’s on xmm0 and xmm1, so we can use them to temporarily hold values. That will push the stack down a further 16×2 or 32 bytes.
- Finally, we’ll want to back up whatever is being stored in eax, ebx, and ecx since the Predator system will be using those to store return values. That’ll be an additional 4×3 or 12 bytes the stack will be pushed down.
Adding everything up, that’s a total of 46 bytes the stack will be shifted down. This will change the location of the movement offsets to [esp+5E], [esp+62], and [esp+66] following our stack preservation efforts.
Other than the movement offsets, we’ll need to know who actually is moving. That information can be found in the esi register, which points to the moving creature’s location structure.
Dimensional scale information is only required for games that have true scaling implemented. This is a topic that will receive some extended detailing from me in that Predator specific article when it is written.
One last thing before we start: we’ll want to ensure that the Predator function only goes off when a non-player entity is moving. One of these days I’ll add a player-side Predator function, but today is not that day. We’ll also want to make sure that the player location pointer has been created at the time this hook is executing, as it may very well not be yet (this function will be called very frequently).
And that’s what we’ll do at the start of our code. We will check to see if the player location pointer has been created and if the player is the one moving. If everything looks good, we’ll preserve all necessary data to the stack.
Predator Initiation Hook – First Steps
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | initiatePredator: pushf // Ensure that the player location structure has been initialized. cmp [playerLocation],0 je initiatePredatorOriginalCode // Ensure that the moving entity is not the player. cmp [playerLocation], esi je initiatePredatorOriginalCode // Backing up a few SSE registers to hold temporary values. sub esp ,10 movdqu [ esp ], xmm0 sub esp ,10 movdqu [ esp ], xmm1 // Backing up the registers used by the Predator system to store its // return values. push eax push ebx push ecx |
Not having to worry about memory distances greater than 2GB (which isn’t going to happen in 32-bit land) really makes the code a bit more compact! Normally we have to load pointer addresses into registers and then dereference them, but we can safely just do everything inline. This is nice! Let’s not get used to it.
Anyway, very basic code here that looks like other initial Predator implementation bits of code. Now the fun part: parameter preparation and Predator function execution. I have edited the Omnified framework so that it is now 32-bit compatible, but there are still some things we must be aware of when passing data to these functions that were originally written for 64-bit applications. Instances of these “things” will be called out in the comments.
Predator Initiation Hook – Function Execution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | initiatePredatorExecute: // This game lacks true scale, so we just prepare an identity matrix // full of 1's to pass as the dimensional scale matrix parameter. movss xmm1 ,[identityValue] shufps xmm1 , xmm1 ,0 // Let's load the movement offsets from the stack now before we affect // the stack any more than we already have. movups xmm0 ,[ esp +5E] // We will now push the player's coordinates to the stack as the first // parameter. In addition to pushing the X, Y, and Z coordinates, which // must be pushed individually in reverse order since only 4 bytes of data // are pushed at a time since this is 32-bit. // We must also push the 4 bytes of memory found immediately after the // Z coordinate. // We have to do this because Omnified functions were written with the // expectation that the smallest amount of data that could be pushed // to the stack at a time would be 8 bytes (which is typically the case // with 64-bit applications). mov eax ,[playerLocation] push [ eax +44] push [ eax +40] push [ eax +4C] push [ eax +48] // We now push the moving creature's coordinates to the stack, keeping in // mind the fact that we have to make 4 pushes for a total of 16 bytes. push [ esi +44] push [ esi +40] push [ esi +4C] push [ esi +48] // We now dump the entire identity matrix to the stack for the scale // parameter. The Predator system, once again, expects 16 bytes. sub esp ,10 movdqu [ esp ], xmm1 // Now it is time to pass the movement offsets parameter. We currently have // them loaded into xmm0, however we need to provide the values to the // Predator system as if they were being pushed directly from memory. The // following code accomplishes this by putting xmm0's high words onto the // xmm1 register and then moving the xmm0 and xmm1 registers onto the stack // as quadwords. movhlps xmm1 , xmm0 sub esp ,8 movq [ esp ], xmm0 sub esp ,8 movq [ esp ], xmm1 // With all the parameter passed, it is time to call the Predator system. call executePredator |
As the documentation in the code points out, we must take special care when preparing and passing our parameters to the stack, given that we are operating in a 32-bit world, which is not the norm for us.
All that is left now is to take the return values from the Predator system, stuff them back where they belong, and then do some cleanup.
Predator Initiation Hook – Return Value Processing and Cleanup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | initiatePredatorCleanup: // We take the return values from the Predator system and plop 'em directly // back to their counterparts on the stack. mov [ esp +5E], eax mov [ esp +62], ebx mov [ esp +66], ecx pop ecx pop ebx pop eax movdqu xmm1 ,[ esp ] add esp ,10 movdqu xmm0 ,[ esp ] add esp ,10 initiatePredatorOriginalCode: popf movss xmm6 ,[ esp +30] jmp initiatePredatorReturn |
And that’s it. Hooray for Predator integration into Dragon’s Dogma! Hold on though! We need to tweak some external parameters of the Predator system in order to get it to fit right with Dragon’s Dogma‘s very non-granular coordinate system.
External Parameter Tweaking
1 2 3 4 5 6 7 8 9 10 11 12 | threatDistance: dd ( float )150.0 aggroDistance: dd ( float )880.0 // Positive and negative limit enforcement is essentially disabled. positiveLimit: dd ( float )10000.0 negativeLimit: dd ( float )-10000.0 |
The values above were determined through experimentation. Specifically, I measured the average distance between when an enemy would notice you and start to come towards you, and used that to set the boundary of the Area of Sketchiness. Predator system terminology will be covered in the article specifically covering that system. When it is published. Someday.
Does It Work Though Man!?
Yeah! Sort of. Well not completely. After injecting the above hook into the game, I noticed that not all movement was being accelerated. Indeed, walking and running were receiving acceleration, however there is a third movement mode left untouched: sprinting.
The player character is able to toggle between walking and running. Running is simply a faster type of walk that doesn’t use up any stamina. The player can also sprint forward at even faster speeds, and this does use up stamina. Monsters have the same movement modes available to them, and the movement application code we have hooked into only applies to walking and running.
A separate movement application code exists for sprinting — this is not at all normal in the games I’ve hacked. There is typically a single bit of code responsible for applying movement offsets for all types of movement modes. I suspect it’s because I’ve hacked so many games recently that use the Havok physics engine, which this game isn’t using of course.
Luckily we’ve already encountered the movement application code for sprinting; it’s located by one of the other bits of code we saw updating our coordinate values, specifically at DDDA.exe+C5E7E5. We just need to add an initiation point at this location as well.
The code will look fairly identical to the hook we just wrote; the only difference is that the moving creature’s location structure is pointed to by the edi register, and the movement offsets are stored in the xmm2, xmm3, and xmm6 registers.
Predator Initiation Hook for Sprinting
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | // Initiates the Predator system for sprinting movement. // edi: Moving creature's location structure. // xmm2: X movement offset. // xmm3: Y movement offset. // xmm6: Z movement offset. // xmm0: Value following Z movement offset. define (omnifyPredatorSprintingHook, "DDDA.exe" + C5E7E5) assert (omnifyPredatorSprintingHook, F3 0F 58 57 40) alloc (initiatePredatorSprinting,$1000, omnifyPredatorSprintingHook) registersymbol (omnifyPredatorSprintingHook) initiatePredatorSprinting: pushf // Ensure that the player location structure has been initialized. cmp [playerLocation],0 je initiatePredatorSprintingOriginalCode // Ensure that the moving entity is not the player. cmp [playerLocation], edi je initiatePredatorSprintingOriginalCode // Backing up a SSE registers to hold temporary values. sub esp ,10 movdqu [ esp ], xmm1 // Backing up the registers used by the Predator system to store its // return values. push eax push ebx push ecx initiatePredatorSprintingExecute: // This game lacks true scale, so we just prepare an identity matrix // full of 1's to pass as the dimensional scale matrix parameter. movss xmm1 ,[identityValue] shufps xmm1 , xmm1 ,0 // Pushing the player's coordinates as the first parameter. // See the documentation in the main Predator initiation hook for // more information. mov eax ,[playerLocation] push [ eax +44] push [ eax +40] push [ eax +4C] push [ eax +48] // Pushing the moving creature's coordinates to the stack for the second // parameter. push [ edi +44] push [ edi +40] push [ edi +4C] push [ edi +48] // We dump the identity scaling matrix onto the stack as the third parameter. sub esp ,10 movdqu [ esp ], xmm1 // We now dump the movement offsets to the stack for the fourth parameter. // We have to do it in reverse paired order as the Predator system expects // 8 byte pushes. sub esp ,4 movd [ esp ], xmm3 sub esp ,4 movd [ esp ], xmm2 sub esp ,4 movd [ esp ], xmm0 sub esp ,4 movd [ esp ], xmm6 // With everything loaded we call the Predator system. call executePredator initiatePredatorSprintingCleanup: // We take the return values from the Predator system and plop 'em directly // back to their counterparts. movd xmm2 , eax movd xmm3 , ebx movd xmm6 , ecx pop ecx pop ebx pop eax movdqu xmm1 ,[ esp ] add esp ,10 initiatePredatorSprintingOriginalCode: popf addss xmm2 ,[ edi +40] jmp initiatePredatorSprintingReturn omnifyPredatorSprintingHook: jmp initiatePredatorSprinting initiatePredatorSprintingReturn: |
Now all important modes of movement appear to be covered! Goblins rush at me with insane fury and increased determination! Fantastic. We would be done at this point, but…
Man, Those Pawns Are Sure Annoying
In Dragon’s Dogma, your hero is accompanied by a special type of henchman, known as a pawn. They are not quite human…I’m not sure what they are. But they basically attached to your ass throughout the game.
Literally. They are attached to your butt. And they really, really are insistent on following your ass no matter where you go. With the Predator system enabled, I actually found them to be incredibly annoying. Like a hyperactive doggy following you when you don’t want it to, trying to get your attention.
So I believe we need to exempt pawn movement from being influenced by the Predator system. And that’s what we’re going to do, baby. First, however, we need to isolate the data structures that belong to the pawn from the rest of the entities in the game.
I figure that there probably exists code in the game that exclusively accesses the pawn’s health, and we could use that to find the pawn’s data. To do this, I first look for code that is accessing my player’s own health, and get the following:
I’m going to take that window showing the code accessing our player’s health, and compare that to what we get when we look at one showing the code accessing my pawn’s health:
We see a number of the same instructions accessing our pawn’s health as well, which is to be expected since most of the instructions accessing the player’s health are general “health polling” functions that look at everything on the map.
But there are also more than a few additional instructions that are accessing our pawn’s health and not ours. It is my hope that one of these instructions belong to a function purposed for the exclusive polling of our pawn’s health.
After going through them, we find that the last instruction listed in the image, located at DDDA.exe+33273D, is indeed just that. I determined it was an exclusive pawn polling function by opening up the disassembler and then seeing what other addresses that instruction accesses. The only address listed is our pawn’s, so that’s good enough for me for now. Further playtesting will confirm how solid this function is.
We will go ahead then and create a data retrieval hook for the pawn at this location. This will appear somewhat similar in form to the player data hook, the main difference being in the pointers we’ll be saving the addresses to.
Pawn Hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | // Gets information about the player's pawn. // eax: Address of the pawn's health struct. define (omniPawnHook, "DDDA.exe" + 33273D) assert (omniPawnHook, F3 0F 10 40 08) alloc (getPawn,$1000, omniPawnHook) alloc (pawnHealth,8) alloc (pawnLocation,8) registersymbol (omniPawnHook) registersymbol (pawnHealth) registersymbol (pawnLocation) getPawn: pushf push ebx mov [pawnHealth], eax // The location structure can be found in the health structure at offset // 0x1B4. mov ebx ,[ eax +1B4] mov [pawnLocation], ebx pop ebx getPawnOriginalCode: popf movss xmm0 ,[ eax +08] jmp getPawnReturn omniPawnHook: jmp getPawn getPawnReturn: |
With this hook in place we can add the necessary exclusionary logic to our two Predator initiation points so that the pawn will no longer be boosted and act like an annoying puppy. Woot!
So, How’s It Look?
Oh it looks good. Enemies now move at bullet speed as they’re homing in on your butt. We’re going to be getting swarmed hardcore by lots of evil, nasty goblins and other assorted baddies. Let’s take a look at what kind of difference the Predator system makes:
Before Predator
After Predator
Now enemies not only have the hitting power of the Apocalypse system, they can get to us faster to deliver those hits quicker too!
Here’s the complete code required for the implementation of the Predator system into Dragon’s Dogma:
Predator Initiation Hooks – Complete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | // Initiates the Predator system. // esi: Moving creature's location structure. // [esp+5E]: Start of movement offsets set. define (omnifyPredatorHook, "DDDA.exe" + 44D030) assert (omnifyPredatorHook, F3 0F 10 74 24 30) alloc (initiatePredator,$1000, omnifyPredatorHook) alloc (identityValue,8) registersymbol (omnifyPredatorHook) initiatePredator: pushf // Ensure that the player location structure has been initialized. cmp [playerLocation],0 je initiatePredatorOriginalCode // Ensure that the moving entity is not the player. cmp [playerLocation], esi je initiatePredatorOriginalCode // Ensure that the moving entity is not the pawn. cmp [pawnLocation], esi je initiatePredatorOriginalCode // Backing up a few SSE registers to hold temporary values. sub esp ,10 movdqu [ esp ], xmm0 sub esp ,10 movdqu [ esp ], xmm1 // Backing up the registers used by the Predator system to store its // return values. push eax push ebx push ecx initiatePredatorExecute: // This game lacks true scale, so we just prepare an identity matrix // full of 1's to pass as the dimensional scale matrix parameter. movss xmm1 ,[identityValue] shufps xmm1 , xmm1 ,0 // Let's load the movement offsets from the stack now before we affect // the stack any more than we already have. movups xmm0 ,[ esp +5E] // We will now push the player's coordinates to the stack as the first // parameter. In addition to pushing the X, Y, and Z coordinates, which // must be pushed individually in reverse order since only 4 bytes of data // are pushed at a time since this is 32-bit. // We must also push the 4 bytes of memory found immediately after the // Z coordinate. // We have to do this because Omnified functions were written with the // expectation that the smallest amount of data that could be pushed // to the stack at a time would be 8 bytes (which is typically the case // with 64-bit applications). mov eax ,[playerLocation] push [ eax +44] push [ eax +40] push [ eax +4C] push [ eax +48] // We now push the moving creature's coordinates to the stack, keeping in // mind the fact that we have to make 4 pushes for a total of 16 bytes. push [ esi +44] push [ esi +40] push [ esi +4C] push [ esi +48] // We now dump the entire identity matrix to the stack for the scale // parameter. The Predator system, once again, expects 16 bytes. sub esp ,10 movdqu [ esp ], xmm1 // Now it is time to pass the movement offsets parameter. We currently have // them loaded into xmm0, however we need to provide the values to the // Predator system as if they were being pushed directly from memory. The // following code accomplishes this by putting xmm0's high words onto the // xmm1 register and then moving the xmm0 and xmm1 registers onto the stack // as quadwords. movhlps xmm1 , xmm0 sub esp ,8 movq [ esp ], xmm0 sub esp ,8 movq [ esp ], xmm1 // With all the parameter passed, it is time to call the Predator system. call executePredator initiatePredatorCleanup: // We take the return values from the Predator system and plop 'em directly // back to their counterparts on the stack. mov [ esp +5E], eax mov [ esp +62], ebx mov [ esp +66], ecx pop ecx pop ebx pop eax movdqu xmm1 ,[ esp ] add esp ,10 movdqu xmm0 ,[ esp ] add esp ,10 initiatePredatorOriginalCode: popf movss xmm6 ,[ esp +30] jmp initiatePredatorReturn omnifyPredatorHook: jmp initiatePredator nop initiatePredatorReturn: identityValue: dd ( float )1.0 threatDistance: dd ( float )150.0 aggroDistance: dd ( float )880.0 // Positive and negative limit enforcement is essentially disabled. positiveLimit: dd ( float )10000.0 negativeLimit: dd ( float )-10000.0 // Initiates the Predator system for sprinting movement. // edi: Moving creature's location structure. // xmm2: X movement offset. // xmm3: Y movement offset. // xmm6: Z movement offset. // xmm0: Value following Z movement offset. define (omnifyPredatorSprintingHook, "DDDA.exe" + C5E7E5) assert (omnifyPredatorSprintingHook, F3 0F 58 57 40) alloc (initiatePredatorSprinting,$1000, omnifyPredatorSprintingHook) registersymbol (omnifyPredatorSprintingHook) initiatePredatorSprinting: pushf // Ensure that the player location structure has been initialized. cmp [playerLocation],0 je initiatePredatorSprintingOriginalCode // Ensure that the moving entity is not the player. cmp [playerLocation], edi je initiatePredatorSprintingOriginalCode // Ensure that the moving entity is not the pawn. cmp [pawnLocation], edi je initiatePredatorSprintingOriginalCode // Backing up a SSE registers to hold temporary values. sub esp ,10 movdqu [ esp ], xmm1 // Backing up the registers used by the Predator system to store its // return values. push eax push ebx push ecx initiatePredatorSprintingExecute: // This game lacks true scale, so we just prepare an identity matrix // full of 1's to pass as the dimensional scale matrix parameter. movss xmm1 ,[identityValue] shufps xmm1 , xmm1 ,0 // Pushing the player's coordinates as the first parameter. // See the documentation in the main Predator initiation hook for // more information. mov eax ,[playerLocation] push [ eax +44] push [ eax +40] push [ eax +4C] push [ eax +48] // Pushing the moving creature's coordinates to the stack for the second // parameter. push [ edi +44] push [ edi +40] push [ edi +4C] push [ edi +48] // We dump the identity scaling matrix onto the stack as the third parameter. sub esp ,10 movdqu [ esp ], xmm1 // We now dump the movement offsets to the stack for the fourth parameter. // We have to do it in reverse paired order as the Predator system expects // 8 byte pushes. sub esp ,4 movd [ esp ], xmm3 sub esp ,4 movd [ esp ], xmm2 sub esp ,4 movd [ esp ], xmm0 sub esp ,4 movd [ esp ], xmm6 // With everything loaded we call the Predator system. call executePredator initiatePredatorSprintingCleanup: // We take the return values from the Predator system and plop 'em directly // back to their counterparts. movd xmm2 , eax movd xmm3 , ebx movd xmm6 , ecx pop ecx pop ebx pop eax movdqu xmm1 ,[ esp ] add esp ,10 initiatePredatorSprintingOriginalCode: popf addss xmm2 ,[ edi +40] jmp initiatePredatorSprintingReturn omnifyPredatorSprintingHook: jmp initiatePredatorSprinting initiatePredatorSprintingReturn: |
With that, the game is about 2/3rds baseline Omnified. The only remaining system to put in before the game is deemed streamable is the Abomnification system, which causes creatures to change shape and size randomly. I don’t believe that will be difficult at all, given that this game has built-in easy scaling support.
Until then, I hope you found this article interesting and informative, maybe even a bit of an entertaining read. If you need to see this brutal destructive gameplay live, don’t forget that the only place to do so is on my stream at https://twitch.tv/omni!
If you have any questions feel free to reach out on my Discord server at https://discord.gg/omni!
Until next time then, keep it real folks.
~Omni