The Abomnification system is a game-neutral Omnified process that causes NPCs of any game its injected into to constantly change shape and size — making the viewer feel like they might have accidently dropped some acid. Once a creature has finished morphing to a new shape and size, it will immediately begin morphing to another shape and size.
While this is lovely, it doesn’t allow us to fully appreciate the new horrific (and quite temporary) appearance of the mutated NPC. Also, after hours and hours of playing games with constantly morphing creatures, it can sometimes be a bit much when there is no stability at all to the very makeup of the creatures constantly occupying my screen.
So to remedy this, I added a little TODO item to my list of TODOs, in particular one where I would implement a brief period of rest between the point of a morph finishing up and the next morph beginning. This, I believe, should make the whole experience much more palatable to viewers.
I’m not entirely sure how complicated this will be, or really whether it warrants an article. Fortunately for all of us, I also don’t care! It should be an interesting little exercise, so let’s get to it and figure out how we can implement pausing between morphs with my lovely Abomnification system.
How Morphs Are Sequenced
At the time of writing, I have not yet published the definitive Abomnification system article. One day it shall appear here, I swear! When it does, you’ll find it under the Omnified Design section of my hackpad.
To make up for this lack of background information, let’s summarize briefly how the whole morphing business works. At the start of a morphing cycle, a target width, height, and depth are generated randomly, with a number of factors influencing the particulars of the random generation. These target dimensions are stored in the creature’s morphing scale data memory as the new morphing targets.
A number of morphing steps are then generated, with each step consisting of new values for the width, height, and depth that are just a bit closer to the morphing targets than the previous step. As we “walk” down each step, we set the creature’s actual dimensions to reflect the values of said step. This is how we achieve the smooth animation between morphing targets.
Once we reach the morphing targets, we start all over again from the beginning with the next cycle: immediately generating new morphing targets, and then proceeding to change the creature’s dimensions, step-by-step. In the end, we have something that looks like this:
All done in assembly language, mind you.
What I’d like to do is have the morphing pause between each cycle. I want the pause to be individual to the particular creature that is morphing; the pause will have no effect on morphing as whole globally. Let’s get to adding this functionality to the primary Abomnification system function then.
Changes to the Omnified Framework Library
I keep all my game-neutral code within my Omnified framework library file: Omnified.lua. As I keep on parroting in articles like this, I don’t yet have the Omnified hacking code uploaded to the Bad Echo source repository, but I will do so soon!
The bulk of the code in this file isn’t actually LUA, mind you, but assembly. As I also continue to parrot: I’ll give a nice article eventually in the future as to why it is all organized like this. Until then, you’ll have to just deal with it my friend!
All of that aside, let’s add our desired pausing feature to the main Abomnification system function:
How Are We Going to Do This?
The knee-jerk idea from many a developer (most likely used to dealing with more “normal” code, i.e. not assembly) would be to use a timer in order to stave off the morphing until said timer elapses. While I do make use of timers and essentially spawn them from assembly for other things, we certainly don’t want to do that here. Abomnification code tends to run in heavily executed areas of the program, literally being executed hundreds to thousands of times per second for an individual character in the game.
And that is for just that character, sometimes we’re morphing upwards of 100 creatures on the screen at once. While I guess we could spawn off literally thousands of different timers after every few seconds to control the morphing, that just seems a bit untenable to me — most importantly however, it would just be a bit damn inconvenient to implement in the assembly for an uncountable number of reasons.
Instead, we’re going to make use of the “timing mechanism” we already use to implement the morphing itself. It is the most primitive of timing mechanisms, one best shied away from in most situations, but also one perfectly acceptable in this strange case we’ve created for ourselves. I shall summarize.
Currently, the morphing timing is achieved by maintaining an integer count value in memory representing the current step and then decrementing it until it runs out. As soon as it runs out then it is time for a new morph. The trigger behind each step is actually the rendering code itself running into our injected code. We prepare the steps ahead of time in light of whatever the maximum value of the creatures morphing steps counter is, and it all works together lovely.
So we’re going to simply add another phase to all of this. They will share the same periods, defined by the value for the creature that holds the number of steps between morphs. During the first phase, the creature will morph. During the second phase, the creature will not. This means that the duration of the pause will be the same as the duration of the morph, and that’s just fine with me.
Adding a Pause Phase to the Abomnification Assembly
The first thing we’ll need to do is add a capability to track which particular phase we’re on. Because this consideration is per-creature, we’ll need to add some data to the creature’s own morphing scale data. The morphing scale data for each creature is currently 48 bytes, consisting of a number of integer values (and a few floating point values), and it is sandboxed from the rest of the game’s memory to avoid both interference and us overwriting something critical. More information on this can be found by consulting the Abomnification design article.
For convenience’s sake, I like dealing with 4 byte integers and nothing smaller in this particular type of situation. So, we’re going to need to add an additional 4 bytes to the morph scale data. There’s a few places in the code where we need to know how large the morph scale data is, the first being the code that actually allocates the entire morph scale data region itself.
Currently, the morph scale data memory region must occupy an area large enough to accommodate a resolution address of anywhere between
0xFFFF. This is due to the method I’ve developed to resolve the location in memory to a particular creature’s morph scale data based on another memory address, one that is allocated by the game itself and guaranteed to be unique to the creature. The mechanics for that as well will be further discussed in that elusive Abomnification design article I keep mentioning.
As it stands now, in order to accommodate all these resolution addresses, we require the morph scale data to be sized at 48 bytes x
0xFFFF entries or
0x2FFD0. Because we’re shifting to 52 bytes now, we’ll need to up that to
Abomnification Code – Updating Morph Scale Data Size
// Abomnification Scale Data alloc(morphScaleData,$33FFCC) alloc(morphScaleIndex,8) registersymbol(morphScaleData) registersymbol(morphScaleIndex) morphScaleIndex: dd 0
After updating this, we’ll need to propagate these changes to another area of code which concerns itself with the size of morph scale data entries. The code which I speak of is the “scrubbing” code that cleans up the morph scale data when a command to do so is executed.
We’ll need to update the range of memory that is looped through and scrubbed, as well as add a new entry where the phase will be held so that it itself gets scrubbed.
Abomnification Code – Updating Memory Scrub to New Morph Scale Data Size
scrubMorphData: mov [forceScrub],0 mov [morphScaleIndex],0 push rax mov rax,0 nextMorphDataScrub: add rax,#52 // Updated sizes. cmp rax,0x33FFCC je exitMorphDataScrub mov rdx,morphScaleData add rdx,rax mov [rdx],0 mov [rdx+4],0 mov [rdx+8],0 mov [rdx+C],0 mov [rdx+10],0 mov [rdx+14],0 mov [rdx+18],0 mov [rdx+1C],0 mov [rdx+20],0 mov [rdx+24],0 mov [rdx+28],0 mov [rdx+2C],0 // New phase data mov [rdx+30],0 jmp nextMorphDataScrub exitMorphDataScrub: pop rax
This code is never ran automatically, only on demand from myself, and rather infrequently these days at that. Usually only when a creature is morphing in a manner that doesn’t please me.
Finally, we’ll need to update the piece of code that actually performs the creature data resolution that I spoke of earlier. In order for it to calculate the address in memory that holds a creature’s data, it must be aware of the size of each creature’s morph scale data. Previously that was 48 bytes, and now it is 52 bytes.
Abomnification Code – Updating Morph Scale Data Resolution
applyMorphScaleFromData: mov rdx,morphScaleData push rax mov rax,#52 // Updated entry size. movzx ecx,bx imul eax,ecx mov rcx,rax pop rax add rdx,rcx cmp [rdx],0 jbe initializeMorphSteps cmp [stopMorphs],1 je executeAbomnificationCleanup dec [rdx] jmp stepMorph
Now that we’ve taken care of these memory particulars, let’s add the actual pause phase functionality to the code. We first need to deal with the new entry in the morph scale data itself. It actually does not require us to define a new symbol or anything like that, all we need to do is be cognizant that this phase data is stored at an offset of
The Abomnification system function will first check the value of the current step counter for the creature. If it is zero, it will reset the step counter and generate new morph targets. If it is not zero, it will apply the current step’s dimensions. We are going to change this process up to suit our purposes. Let’s reimagine it to instead be the following: if the step counter is 0, it will reset the step counter and generate new morph targets; otherwise, it will execute the current phase.
If the phase is not the pause phase, it will apply the stepped morph. If it is the pause phase, it’ll simply do nothing. How simple is that!
When we normally want to execute a stepped morph, we jump to
stepMorph, as can be seen at the bottom of the previous code snippet. We’ll instead jump to
executeMorphPhase. Let’s then add a new label with this name, and place it right above where
Abomnification Code – Executing the Morph Phase
executeMorphPhase: // Current phase is located in [rdx+30]. // 0 == Pause phase. Do nothing! // 1 == Step morph phase. Continue morphing! cmp [rdx+30],1 je stepMorph jmp executeAbomnificationCleanup stepMorph:
Well, who said pausing was hard! Basically, if the phase is set to the pause phase, all we need to do is shift execution to the tail portion of the function where cleanup occurs and execution exits Omnified land. This little bit of code we see up above also serves as a potential start for some other interesting effects and features we can add later on to the system that are phase based.
So what will actually set the phase? The place that makes the most sense is the same area of code that is evaluating the state of the step counter, determining whether or not to reset the step counter and generate new morphing targets. We’ll insert some code right before the jump to the morphing target generation which will change the phase, essentially toggling it.
Abomnification Code – Changing the Phase
applyMorphScaleFromData: mov rdx,morphScaleData push rax push rbx mov rax,#52 movzx ecx,bx imul eax,ecx mov rcx,rax pop rbx pop rax add rdx,rcx cmp [rdx],0 ja continuePhase // Current phase is located in [rdx+30]. // 0 == Pause phase. Switch to step morph phase. // 1 == Step morph phase. Switch to pause phase. cmp [rdx+30],1 je changeToPausePhase mov [rdx+30],1 jmp initializeMorphSteps changeToPausePhase: mov [rdx+30],0 jmp initializeMorphSteps continuePhase: cmp [stopMorphs],1 je executeAbomnificationCleanup dec [rdx] jmp executeMorphPhase initializeMorphSteps:
The reason why we have the pause phase associated with zero is because during a creature’s first pass the freshly allocated memory holding the phase will be zero, which means the phase will be immediately switched to the step morph phase and morphing can begin. This will allow us to avoid having to wait for a whole cycle with nothing happening whenever we switch on the Abomnification system.
Also note that even when we switch to the pause phase, we still need to reset the step counter, since doing otherwise would result in a pause phase that only lasted a single execution pass.
That being said…we are done! If everything doesn’t crash and burn as soon as we try it out, we should now have a pause phase added to the morphing process!
The End Result
Lo and behold! I have injected some sanity into the cracks of my Abomnification system. We can now observe pauses occurring between each morph cycle. Time for me to mark this little item off on my TODO list.
I’ll be very much looking forward to playing Omnified games with this new bit of functionality added in. It just makes everything so much more palatable in this strange, strange universe we’ve created.
To get a better idea of what this new kind of morphing looks like on the many different characters one encounters in a game, the place you’ll want to check out is my Twitch stream. Give me a follow if you aren’t already following, and say hello sometime! I always very much appreciate it.
Until next time, as always, thank you for your interest and your time! Have a good one friends.