NPC Hacking

Jul 2, 2008 at 1:11 AM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
I'm really glad Runelancer gave us that offsets. I tried to code a new enemy, I was using the First Cave Critter (Nummer 46 I guess), the offset is 0x0033C00.

Here is my code:

Code:
 push ebp
mov ebp, esp
push 0x01
push 0x01
call 0x020640
nop
nop
[...]
nop
nop
mov esp, ebp
pop ebp
ret

Just ignore the NOP's please ;D Since I need space I just filled the uneeded one with them.

So, it does absolutly nothing except playing a very annoying sound every(?) frame. It would be nice if someone could explain how to move it, or use the timer to make it move every third frame or something like that. Or even how to make it display another animation. Or explain how to bind framerects into it ;)

Anyone? Runelancer? Cookie? Someone different?
 
Jul 2, 2008 at 4:47 AM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
You're on the right track. But it's MUCH easier to just take an existing enemy and gut the pieces you don't need or add the ones you do need than to start from scratch. Takes a lot of effort to build it from scratch - a good understanding of how the enemy data is laid out in memory is vital here.

What your code is doing, aside from the usual setup, is calling the "play sound" subroutine with "0x01" and "0x01" as parameters: sound mode 1, sound effect ID 1. The NOP instructions are just padding - it's a "no operation" instruction which does nothing but idle the processor for a cycle.

This function is called every frame, so in your case you're constantly telling the game to play sound ID 0x01, once every frame. Which is almost certainly a total pain in the ass. And you're not seeing anything either because you're never telling the game to draw anything, so the entity isn't very useful (aside, maybe, to annoy the player. ;) )

I'm going to write an NPC hacking guide - seems it'll be a bit of an easier task if you and others interested in this know a bit of assembly. :o In the meantime, I recommand having a look at some of the simpler entities and trying to modify them.
 
Jul 2, 2008 at 5:10 AM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
Ah yes...

Code:
0049BCA8	EffectObj[0x00].InUse		+0x00		0x44 in len
0049BCAC	EffectObj[0x00].ID		+0x04
0049BCB0	EffectObj[0x00].Mode		+0x08
0049BCB4	EffectObj[0x00].X			+0x0C
0049BCB8	EffectObj[0x00].Y			+0x10
0049BCBC	EffectObj[0x00].MoveX		+0x14
0049BCC0	EffectObj[0x00].MoveY		+0x18
0049BCC4	EffectObj[0x00].WasInit		+0x1C
0049BCC8	EffectObj[0x00].			+0x20 ; Unused?
0049BCCC	EffectObj[0x00].FrameID		+0x24 ; This is the actual frame to display, from the rects.
0049BCD0	EffectObj[0x00].FrameTimer	+0x28
0049BCD4	EffectObj[0x00].XOffset		+0x2C
0049BCD8	EffectObj[0x00].YOffset		+0x30
0049BCDC	EffectObj[0x00].Display_L	+0x34
0049BCE0	EffectObj[0x00].Display_U	+0x38
0049BCE4	EffectObj[0x00].Display_R	+0x3C
0049BCE8	EffectObj[0x00].Display_D	+0x40

Straight outta my notes!

What's interesting to you, here, is the +0x?? part. For instance, to mess with the X/Y position, you'd access the enemy data through this...

mov edx, [ebp + 0x08] ; This is the enemy pointer.
mov eax, [edx + 0x0C] ; (enemy pointer) + 0x0c bytes: x position
mov ecx, [edx + 0x10] ; (enemy pointer) + 0x10 bytes: y position

This places the x position in eax and the y position in ecx. You can also write back to those locations like that, and changes those values (for instance, changing the X/Y positions.)

What you do is grab the pointer to the NPC object (all NPCs are passed this through [ebp + 0x08]), then add to that the amount you need to get to the member you want (ex, if you MOVed the pointer into register edx, you'd do [edx + 0x0C] to get the X position.)

mov edx, [ebp + 0x08] ; This is the enemy pointer.
mov [edx + 0x0C], eax ; Now we're WRITING eax into the enemy's X position.

And that above is an example of writing back to this data.
 
Jul 2, 2008 at 12:16 PM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
Thank you very much.
Do you know a way to make this code work with the primer?

"Unrecognized Parameters - mov with edx"

Is the number I write to the X position relative?
If so, how to subtract it?
 
Jul 2, 2008 at 1:31 PM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
Primer: a textbook formerly used in primary education (often pronounced "primmer" in General American) to teach the alphabet and other basic concepts.

A primer is more or less a basic introductory guide to something. I'm not sure what you're talking about here...
 
Jul 2, 2008 at 2:37 PM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
RuneLancer said:
A primer is more or less a basic introductory guide to something. I'm not sure what you're talking about here...

I think you called it primer.

It's called Discrete.exe
 
Jul 2, 2008 at 11:08 PM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
That's the assembly pseudo-compiler that was coded for the assembly primer I was going to write. ;) Now I know what you meant.

Discrete is a little anal about formatting, and doesn't support every command. It's incomplete but covers all the instructions a basic hack'll need. I may rewrite it eventually. Anyway. IIRC, the syntax is...

X OP P1,P2

X: A label for jumps. Optional - put in a space if you're not using it (so there'll be two spaces in front of OP)
OP: The operator (ex, "mov")
P1: Parameter 1
P2: Parameter 2, seperated from P1 by just a comma (no spaces)

Also, parameters can't have spaces in them (such as with [ebp+0x08], rather than [ebp + 0x08]) and numbers must be written in hex using 0x notation (0x08 is good; #$08, 8, &H08 all aren't.)

You can just add two spaces in front of each line in my examples and remove the spaces after the comma and inside the brackets, and it'll go through just fine (even if you leave in the comments.)
 
Jul 4, 2008 at 12:16 AM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
00433E88 C7 40 14 01 FA FF FF mov [eax+0014],FFFFFA01

Makes the critter jump into the air.
The lower the number is, the higher it jumps, but why that?
Hmmm, your notes say that +0x14 is Move X and not Move Y, isn't that wrong?
 
Jul 4, 2008 at 12:48 AM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
004a6234 Event[0x00].MoveY +0x14

You sure about that? :rolleyes:
 
Jul 4, 2008 at 1:00 AM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
RuneLancer said:
004a6234 Event[0x00].MoveY +0x14

You sure about that? :p

Not 100%.
I'll do further tests :rolleyes:
 
Jul 4, 2008 at 1:16 AM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
00433EFB 8B 4D 08 mov ecx,[ebp+0008]
00433EFE 8B 51 14 mov edx,[ecx+0014]
00433F01 83 C2 40 add edx,40
00433F04 8B 45 08 mov eax,[ebp+0008]
00433F07 89 50 14 mov [eax+0014],edx

Again, this code seems to work with Move Y.
The higher the number is you add to edx, the higher is the gravitation, at a specific point it turns negative and the critters start to float :rolleyes:

Edit: Whats +0x68, +0x74 and +0x78?
 
Jul 4, 2008 at 3:34 AM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
Yes, that IS the Y velocity, as I posted previously. >"<

Code:
004a6228	Event[0x00].X			+0x08
004a622C	Event[0x00].Y			+0x0C
004a6230	Event[0x00].MoveX			+0x10
004a6234	Event[0x00].MoveY			+0x14

And that's what's in my notes. I'm not sure what you're talking about. :/ +0x14 is not MoveX.
 
Jul 4, 2008 at 7:38 AM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
Ahhh, now it works.

RuneLancer said:
Ah yes...

Code:
0049BCA8	EffectObj[0x00].InUse		+0x00		0x44 in len
0049BCAC	EffectObj[0x00].ID		+0x04
0049BCB0	EffectObj[0x00].Mode		+0x08
0049BCB4	EffectObj[0x00].X			+0x0C
0049BCB8	EffectObj[0x00].Y			+0x10
0049BCBC	EffectObj[0x00].MoveX		+0x14
0049BCC0	EffectObj[0x00].MoveY		+0x18
0049BCC4	EffectObj[0x00].WasInit		+0x1C
0049BCC8	EffectObj[0x00].			+0x20 ; Unused?
0049BCCC	EffectObj[0x00].FrameID		+0x24 ; This is the actual frame to display, from the rects.
0049BCD0	EffectObj[0x00].FrameTimer	+0x28
0049BCD4	EffectObj[0x00].XOffset		+0x2C
0049BCD8	EffectObj[0x00].YOffset		+0x30
0049BCDC	EffectObj[0x00].Display_L	+0x34
0049BCE0	EffectObj[0x00].Display_U	+0x38
0049BCE4	EffectObj[0x00].Display_R	+0x3C
0049BCE8	EffectObj[0x00].Display_D	+0x40

Straight outta my notes!

That's what you posted before.
 
Jul 4, 2008 at 2:07 PM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
Ah, crap, I see what went wrong. That's the effect object data, not the NPC object data.

My bad. :(
 
Jul 5, 2008 at 1:44 PM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
Straight outta your notes! :D

Code:
+0x00: Event.InUse (N)
+0x08: Event.X (N)
+0x0C: Event.Y (N)
+0x10: Event.MoveX
+0x14: Event.MoveY
+0x28: Event.NPCID (N)
+0x4C: Event.Direction (N)
+0x54: Event.Display_L (N)
+0x58: Event.Display_U (N)
+0x5C: Event.Display_R (N)
+0x60: Event.Display_D (N)
+0x64: Event.FrameNum
+0x68: Event.FrameID
+0x6C: Event.ObjectTimer
+0x74: Event.ScriptState
+0x78: Event.ScriptTimer

(N): NOT conventions - Don't turn them into temporary storage locations.

I didn't knew assembly is that easy. I just didn't knew about things like ebp+0008. I could have done that much more time before :p
Thanks that you teached me some things Runelancer.

But I have a question. How to make it actually DISPLAY something? :p
I've got a blank entity here, but this time with framerects who are pushed into the stack.
So, how to display them?
 
Jul 5, 2008 at 4:31 PM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
To display an entity, you have to define a display rect. Otherwise there's nothing for it to display and it'll just be invisible.

Display_L: left side of the display rect
Display_R: right side of the display rect (NOT width)
Display_U: top side of the display rect
Display_D: bottom side of the display rect (NOT height)

Fill those members up with anything you want. You don't actually have to define rects, you can just calculate the positions by yourself and set them manually. Defining rects the way most entities and such do is the easiest way though, since you can just copy the data.

Though there are different ways to do this (have a look at some of the code near the end of any displayable entity) you can just MOV the contents of the frame you want to display into the display rect of the entity. That'll be enough to get it to show up.
 
Jul 5, 2008 at 9:41 PM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
Doesn't work :/

Code:
 mov edx,[ebp+0008]
mov eax,[ebp-0030]
mov edx,[ebp+0008]
mov [edx+0054],eax
mov edx,[ebp+0008]
mov eax,[ebp-002C]
mov edx,[ebp+0008]
mov [edx+0058],eax
mov edx,[ebp+0008]
mov eax,[ebp-0028]
mov edx,[ebp+0008]
mov [edx+005C],eax
mov edx,[ebp+0008]
mov eax,[ebp-0024]
mov edx,[ebp+0008]
mov [edx+0060],eax
 
Jul 5, 2008 at 10:15 PM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
For starters, don't do unecessary work. You set edx to something at times, never use it, then later overwrite it with a different value - what for? In fact, just MOV [ebp+08] into edx once and never write to that register again (for the rest of this bit of code.)

Generally, the game uses +68 to store the ID of the frame. Most enemies take this value at the end of the code, shift it to the left 4x (ie, multiplies it by 16), then build the offset to load the frame from with it.

Code:
  mov edx,[ebp+0008]
mov ecx,[edx+0068]
shl ecx,0x04 ; Multiply by 16, since there are 16 bytes in a frame
lea eax,[ebp+ecx-70] ; Frame offset = ebp-0x70 + the above

(In the above example, frames went up to ebp-0x70.)

Chances are you may be running into problems if you're not setting the value, I dunno. Maybe it's used somewhere else in the code.

Try using the above code (adapted to your needs) and see if that works better. ecx is free, edx points to the entity, and eax points to the frame.
 
Jul 6, 2008 at 2:01 AM
Hoxtilicious
"Life begins and ends with Nu."
Join Date: Dec 30, 2005
Location: Germany
Posts: 3218
Age: 32
I never thought it will work/it's that easy, but it is!

I love you Runelancer. :D

Thanks very much.

diph.php


Edit: Now, next question. Making the thing move.
Just pushing the MoveY every frame won't make it I figured.
So, help again please? :p

This is my code:

Code:
 mov edx,[ebp+0008]
mov eax,[ebp-0030]
mov [edx+0054],eax
mov eax,[ebp-002C]
mov [edx+0058],eax
mov eax,[ebp-0028]
mov [edx+005C],eax
mov eax,[ebp-0024]
mov [edx+0060],eax
mov edx,[ebp+0008]
mov ecx,[edx+0068]
shl ecx,0x04
lea eax,[ebp+ecx-60]

Hmm, without you I would be kinda lost Rune :p
 
Jul 6, 2008 at 2:53 AM
The Bartender
"All your forum are belong to us!"
Join Date: Jun 18, 2006
Location: Montreal, Canada
Posts: 581
Age: 39
Here's a little bit of physics: to have smooth, realistic movement, you need to use velocities. A velocity is something you add to a position every frame to get a new position. For instance...

Code:
{
x position = x position + x velocity
y position = y position + y velocity
}

This isn't enough though. Imagine you have a tennis ball. Without friction to slow it down, the moment you'd hit the ball it would keep going in the same direction without slowing down at all. So you also need to make the velocity drop each frame.

Code:
{
x velocity = x velocity * 0.975
y velocity = y velocity * 0.975
x position = x position + x velocity
y position = y position + y velocity
}

Here we have a friction of 0.975. The higher the number, the longer it takes to reach 0, the slower the object loses its velocity. Now when we hit our tennis ball, it'll go in a straight line and slow down, stopping suspended in the air. This is enough for most things, but if we have objects that jump or otherwise lose contact with the floor, they'll need gravity.

Code:
{
x velocity = x velocity * 0.975
y velocity = y velocity * 0.975 + 0.1
x position = x position + x velocity
y position = y position + y velocity
}

We have a gravity of 0.1 now. Since we add a small value every frame, the y velocity will gradually go from -1 to 0 then up into positive numbers, making the object arc upwards then back down realistically.

This is what MoveX and MoveY are for. :D None of this happens automatically, though: you have to implant that in your code yourself. And this introduces a lot of timing issues; when does your enemy start moving? When does it stop moving? To set this up, you can and should use the ScriptState member. If ScriptState is equal to 0, set the initial velocity and set Script State to 1. If ScriptState is equal to 1, keep updating the position until the velocity drops to 0, then set ScriptState to 2. Etc, etc..

Code:
Iron Crab [094 Giant Jelly]
push ebp
mov ebp,esp
sub esp,0x74
mov [ebp-0070],0x00000110	; WALK B
mov [ebp-006C],0x00000000
mov [ebp-0068],0x00000128
mov [ebp-0064],0x00000020
mov [ebp-0060],0x00000110	; WALK A
mov [ebp-005C],0x00000020
mov [ebp-0058],0x00000128
mov [ebp-0054],0x00000040
mov [ebp-0050],0x00000110	; WALK C
mov [ebp-004C],0x00000040
mov [ebp-0048],0x00000128
mov [ebp-0044],0x00000060
mov [ebp-0040],0x00000110	; WALK A
mov [ebp-003C],0x00000020
mov [ebp-0038],0x00000128
mov [ebp-0034],0x00000040
mov [ebp-0030],0x00000128	; FIRE A
mov [ebp-002C],0x00000020
mov [ebp-0028],0x00000140
mov [ebp-0024],0x00000040
mov [ebp-0020],0x00000128	; FIRE B
mov [ebp-001C],0x00000040
mov [ebp-0018],0x00000140
mov [ebp-0014],0x00000060
mov [ebp-0010],0x00000128	; SPAWN WORM
mov [ebp-000C],0x00000000
mov [ebp-0008],0x00000140
mov [ebp-0004],0x00000020
mov edx,[ebp+0008]		; We'll keep ebp+08 in edx as much as possible.
mov ecx,[edx+0078]		; Increment the script timer. It'll be in ecx for now.
add ecx,0x01
mov [edx+0078],ecx
mov eax,[edx+0074]		; Jump based on the state.
jmp [eax*4+X]
0 cmp ecx,0x64			; --- State 0
jl A
mov [edx+0074],0x00000003
xor ecx,ecx
mov [edx+0078],ecx
A mov eax,[edx+0064]		; Increment the frame counter.
add eax,0x01
mov [edx+0064],eax
cmp eax,0x10
jl E
mov eax,[0049E658]		; Move towards player.
mov ecx,[edx+000C]
cmp eax,ecx
jle B
mov ecx,0x00000C01
nop 
jmp C
B mov ecx,0xFFFFF000
nop
C mov [edx+0014],ecx
xor eax,eax
mov [edx+0064],eax
mov eax,[edx+0068]		; Increment the frame ID.
add eax,0x01
cmp eax,0x04
jl D
xor eax,eax
D mov [edx+0068],eax
E mov eax,[0049E658]		; Is it aligned with the player?
mov ecx,[edx+000C]
sub eax,ecx
cmp eax,0x00000800
jg I
cmp eax,0xFFFFF800
jg F
jmp I
F mov [edx+0074],0x00000001	; --- State 1.a
xor ecx,ecx
mov [edx+0078],ecx
1 cmp ecx,0x14			; --- State 1.b
jg J
jl G
mov eax,[0049E658]		; Are we still aligned with the player?
mov ecx,[edx+000C]
sub eax,ecx
cmp eax,0x00001000
jg J
cmp eax,0xFFFFF000
jg K
G mov eax,[edx+0068]		; Nope. So update the frame.
mov ecx,0x00000004
cmp eax,0x05
je H
inc ecx
H mov [edx+0068],ecx
I jmp L
nop
nop
nop
nop
nop
J xor eax,eax			; Set the state back to 0.
mov [edx+0074],eax
jmp L
K mov [edx+0074],0x00000002	; --- State 2.a
xor ecx,ecx
mov [edx+0078],ecx
2 push 0x01				; --- State 2.b
push 0x10
call 0040F350
add esp,0x08
mov edx,[ebp+0008]
shl eax,0x06
sub eax,0x00000580		; eax = -580 to 580
push 0x00000100
push 0x00
push 0x00
push eax
push 0xFFFFF600
mov eax,[edx+000C]
push eax
mov eax,[edx+0008]
push eax
push 0x0B
call 0046EFD0			; spawn fireball
add esp,0x20
push 0x01
push 0x38
call 00420640			; play sound
add esp,0x08
mov edx,[ebp+0008]
mov ecx,[edx+0078]		; check timer
cmp ecx,0x10
jle M				; exit
mov [edx+0074],0x00000000	; set state to 0
xor eax,eax
mov [edx+0078],eax		; set timer to 0
L jmp M				; exit
3 mov [edx+0074],0x00000004	; --- State 3
push 0x00000100
push 0x00
push 0x00
push 0x00
push 0x00
mov eax,[edx+000C]
push eax
mov eax,[edx+0008]
push eax
push 0x000000EB
call 0046EFD0			; spawn worm
add esp,0x20
push 0x01
push 0x17
call 00420640			; play sound
add esp,0x08
mov edx,[ebp+0008]
mov [edx+0068],0x00000006
4 mov ecx,[edx+0078]		; --- State 4 ; check timer
cmp ecx,0x18
jle M				; exit
mov [edx+0074],0x00000000	; set state to 0
xor eax,eax
mov [edx+0078],eax		; set timer to 0
M mov eax,[edx+000C]		; --- exit - Render
mov ecx,[edx+0014]
add eax,ecx
mov [edx+000C],eax
mov ecx,[edx+0068]
shl ecx,0x04
lea eax,[ebp+ecx-70]
add edx,0x54
mov ecx,[eax]
mov [edx],ecx
mov ecx,[eax+0004]
mov [edx+0004],ecx
mov ecx,[eax+0008]
mov [edx+0008],ecx
mov ecx,[eax+000C]
mov [edx+000C],ecx
mov esp,ebp
pop ebp
ret
X --- 0
--- 1
--- 2
--- 3
--- 4

43A220 55 8B EC 83 EC 74 C7 45 90 10 01 00 00 C7 45 94
43A230 00 00 00 00 C7 45 98 28 01 00 00 C7 45 9C 20 00 
43A240 00 00 C7 45 A0 10 01 00 00 C7 45 A4 20 00 00 00
43A250 C7 45 A8 28 01 00 00 C7 45 AC 40 00 00 00 C7 45 
43A260 B0 10 01 00 00 C7 45 B4 40 00 00 00 C7 45 B8 28
43A270 01 00 00 C7 45 BC 60 00 00 00 C7 45 C0 10 01 00 
43A280 00 C7 45 C4 20 00 00 00 C7 45 C8 28 01 00 00 C7
43A290 45 CC 40 00 00 00 C7 45 D0 28 01 00 00 C7 45 D4 
43A2A0 20 00 00 00 C7 45 D8 40 01 00 00 C7 45 DC 40 00
43A2B0 00 00 C7 45 E0 28 01 00 00 C7 45 E4 40 00 00 00 
43A2C0 C7 45 E8 40 01 00 00 C7 45 EC 60 00 00 00 C7 45
43A2D0 F0 28 01 00 00 C7 45 F4 00 00 00 00 C7 45 F8 40 
43A2E0 01 00 00 C7 45 FC 20 00 00 00 8B 55 08 8B 4A 78
43A2F0 83 C1 01 89 4A 78 8B 42 74 FF 24 85 A6 A4 43 00 
43A300 83 F9 64 7C 0C C7 42 74 03 00 00 00 31 C9 89 4A
43A310 78 8B 42 64 83 C0 01 89 42 64 83 F8 10 7C 33 8B 
43A320 05 58 E6 49 00 8B 4A 0C 39 C8 7E 08 B9 01 0C 00
43A330 00 90 EB 06 B9 00 F0 FF FF 90 89 4A 14 31 C0 89 
43A340 42 64 8B 42 68 83 C0 01 83 F8 04 7C 02 31 C0 89
43A350 42 68 8B 05 58 E6 49 00 8B 4A 0C 29 C8 3D 00 08 
43A360 00 00 7F 46 3D 00 F8 FF FF 7F 02 EB 3D C7 42 74
43A370 01 00 00 00 31 C9 89 4A 78 83 F9 14 7F 33 7C 19 
43A380 8B 05 58 E6 49 00 8B 4A 0C 29 C8 3D 00 10 00 00
43A390 7F 1F 3D 00 F0 FF FF 7F 1F 8B 42 68 B9 04 00 00 
43A3A0 00 83 F8 05 74 01 41 89 4A 68 EB 73 90 90 90 90
43A3B0 90 31 C0 89 42 74 EB 67 C7 42 74 02 00 00 00 31 
43A3C0 C9 89 4A 78 6A 01 6A 10 E8 83 4F FD FF 83 C4 08
43A3D0 8B 55 08 C1 E0 06 2D 80 05 00 00 68 00 01 00 00 
43A3E0 6A 00 6A 00 50 68 00 F6 FF FF 8B 42 0C 50 8B 42
43A3F0 08 50 6A 0B E8 D7 4B 03 00 83 C4 20 6A 01 6A 38 
43A400 E8 3B 62 FE FF 83 C4 08 8B 55 08 8B 4A 78 83 F9
43A410 10 7E 61 C7 42 74 00 00 00 00 31 C0 89 42 78 EB 
43A420 53 C7 42 74 04 00 00 00 68 00 01 00 00 6A 00 6A
43A430 00 6A 00 6A 00 8B 42 0C 50 8B 42 08 50 68 EB 00 
43A440 00 00 E8 89 4B 03 00 83 C4 20 6A 01 6A 17 E8 ED
43A450 61 FE FF 83 C4 08 8B 55 08 C7 42 68 06 00 00 00 
43A460 8B 4A 78 83 F9 18 7E 0C C7 42 74 00 00 00 00 31
43A470 C0 89 42 78 8B 42 0C 8B 4A 14 01 C8 89 42 0C 8B 
43A480 4A 68 C1 E1 04 8D 44 0D 90 83 C2 54 8B 08 89 11
43A490 8B 48 04 89 4A 04 8B 48 08 89 4A 08 8B 48 0C 89 
43A4A0 4A 0C 89 EC 5D C3 00 A3 43 00 79 A3 43 00 C4 A3
43A4B0 43 00 21 A4 43 00 60 A4 43 00

This is how I coded the iron crab boss in the underwater tunnel, in The Original Sin. It's been coded 100% by hand. See if you can learn anything from it - the source is partly commented. To make your task easier, put it in notepad and replace common things by labels ([edx+0068] -> entity.FrameID) It'll make it easier to make sense of things.

Back then, I didn't have Discrete to regenerate the code for me, so there could be some slightly sketchy stuff in there. Nothing beats recalculating EVERY jump in the code because you had to add a line somewhere. So if you see anything weird, yeah, it's to be expected.
 
Top