How to: Change Sprite frames, speeds

Completed patches for Keen4.
Post Reply
User avatar
XkyRauh
Posts: 1114
Joined: Sun Aug 31, 2003 9:14 pm
Location: San Diego, California

How to: Change Sprite frames, speeds

Post by XkyRauh »

DISCLAIMER: This is a general-knowledge sort of bit, in case you want to change which frame #'s are used by a sprite. Keep in mind that this does not PRE-CACHE the sprite into memory (that is something that still needs to be figured out)... For example, if you ask the Poison Slug to use a frame from the Dopefish's frameset, you may receive an error unless you're playing a level that precaches both the Dopefish sprites and the Poison Slug sprites (not an issue if you're using TED5 to create your own levels)

Now, to change your sprites! :)

First, find out the sprite number of the sprite you wanna use in place of whatever existing sprite there is. You get this number by looking through your Keen4 directory and noting the filename. For example, Keen facing right while standing is 4SPR0006, while a Berkeloid facing left about to throw a fireball is 4SPR0387. That number at the end is your sprite number. Keen would be 6, the Berkeloid woudl be 387. We'll use the Berkeloid for this example.

Take your sprite number (387) and add 124 to it, (511,) then convert this new number to HEX (1FF). Turn this into a 2-byte word (01FF = $FF $01) and make note of that.

Next, open your Keen4 disassembly, and open that Actions.doc file. Scroll down and find the action(s) you want to replace. In this example, we'll mess with the Poison Slug. Looking at it, you'll see something like this:

Code: Select all

ACTION slug_actions[] = {
  /* Slug moving routine */
	/* AZ.2012 */
	{	320,	315,	0,	0,
		1,	8,	64,	0,
		NULL,		K:03EA,		G:1847,
		&slug_actions[A_SLUG_MOVE+1]
	},
	/* AZ.2030 */
	{	321,	316,	0,	0,
		1,	8,	64,	0,
		K:0369,		K:03EA,		G:1847,
		&slug_actions[A_SLUG_MOVE]
	},
So for the Slug Moving routine, AZ.2012, we get two piece of information. First, we see that it's a two-frame animation, as evidenced by only two entries: "A_SLUG_MOVE" and "A_SLUG_MOVE+1." Other animations may have 4-8 frames available.
More importantly, we're given two numbers: For the first of the frames of the walk animation, they're 320 and 315. The first, 320, is the sprite number for the Slug moving Left (we can confirm this by doing 320-124=196, which is indeed the "Slug moving left" sprite in our Keen4 folder) ...and the second, 315, is the Slug moving Right. We're gonna patch those numbers to our new ones.

But we need an address!!

See that AZ.2012 up there? Take the 2012 part, and Add 2EE70 in HEX ( 30E82)... that's your patch for facing left, and the value two bytes later (30E84) will be your patch for facing right.

In this example, to conclude, we'll make the left-moving Poison Slug use the "Berkeloid pausing to throw while facing left" frame (#387) for the first of its two-frame walk animation:

Code: Select all

%patch $30E82 $FF $01
Ironically, this might allow you to theoretically make a sprite look different depending on whether it's stunned while moving Left or Right, though I'm not sure if the game differentiates (But check out the "Slug_Stunned" part, for instance--it's got 318 listed for both the L and R positions... hmm...

Also, most importantly, YOU MUST PRE-CACHE YOUR SPRITES. I had to test this example at the Island of Fire, because it has Berkeloids and Slugs in the same place.

Maybe someone else can figure out precaching, to help alleviate this burden. :)

Here's the patch I used while testing this:

Code: Select all

#Poison Slugs, when stunned, look like Berkeloids about to fire Left.
%patch $30EDC $FF $01 #stun type A facing L
%patch $30EDE $FF $01 #stun type A facing R
%patch $30EFA $FF $01 #stun type B facing L
%patch $30EFC $FF $01 #stun type B facing R
The other useful bit about this patch is that you can change the TIMING of the animations. Again, looking at our code:

Code: Select all

ACTION slug_actions[] = {
  /* Slug moving routine */
	/* AZ.2012 */
	{	320,	315,	0,	0,
		1,	8,	64,	0,
		NULL,		K:03EA,		G:1847,
		&slug_actions[A_SLUG_MOVE+1]
	},
	/* AZ.2030 */
	{	321,	316,	0,	0,
		1,	8,	64,	0,
		K:0369,		K:03EA,		G:1847,
		&slug_actions[A_SLUG_MOVE]
	},
We can see that the first frame of the Slug moving is held for a count of 8 (see the 1,8,64,0?) and the second frame is held for a count of 8. Let's make the slug crawl more slowly, by doubling the pause for each frame.

Start with the HEX address we'd need to change the animations (in this case, 30E82 for L, 30E84 for R) and add 10 (0A in HEX) to it. So to change the duration for that first walk frame, we'd patch to 30E8C.

The easiest way to think about this is that each of the commands between commas is a two-byte word. So starting at location 30E82, we have:
320, 315, 0, 0,
1, 8, 64, 0,
So in the HEX, it would look like
$20 $03 $15 03 $00 $00 $00 $00
$01 $00 $08 $00 $64 $00 $00 $00
Which is why we add 10 (0A) to the starting location.

For reference, the length of time Keen spends looking up into the sky during his idle sequence is 30, while the time he spends reading his book (before turning the page) is 300. :)

So, to make that Slug spend more time crawling, we'd patch:

Code: Select all

%patch $30E8C $16 $00 #Slug animation speed, frame 1 (default $08 $00)
%patch $30EAA $10 $00 #Slug animation speed, frame 2 (default $08 $00)
Happy editing!
levellass
Posts: 3001
Joined: Wed Oct 11, 2006 12:03 pm
Location: Ngaruawahia New Zealand

Post by levellass »

I don't have the actions.doc file; my disassembly seems to be an earlier version as most of the segments are in .odt format. Does anyone have the actions.doc file?
levellass
Posts: 3001
Joined: Wed Oct 11, 2006 12:03 pm
Location: Ngaruawahia New Zealand

Post by levellass »

Someone suggested I post this here, just as a reference. It's mine and Adis' work on the actions.

Code: Select all

#Action structure:
Int     Description         Values
Int 0:  Left sprite         124-400
Int 2:  Right sprite        124-400
Int 4:  Movement parameter  0-3
Int 6:  Change h            0/1
Int 8:  Change v            0/1
Int 10: Delay (Anim speed)  0-8000
Int 12: H anim move amount  +- any value
Int 14: V anim move amount  +- any value
Int 16: Behavior            [Start of behavior codes only]
Int 18: Behavior segment    [Segment values only]
Int 20: Check sprites       [Start of check sprite codes only]
Int 22: Check segment       [Segment values only]
Int 24: Check tiles         [Start of check tile code only]
Int 26: Check segment       [Segment values only]
Int 30: Next action         [Valid actions only]

The sprite actions consist of 30 bytes in 12 2 or 4 byte chunks.

First chunk: The image used when the sprite is moving or facing left(-1) + 124. The sprite must be cached beforehand to be usable, and must exist in the EGAGRAPH file.

Second chunk: The image used when the sprite is facing or moving right(+1) + 124 The sprite must be cached beforehand to be usable, and must exist in the EGAGRAPH file.

Third chunk: This controls how gravity affects a sprite; it can be anything from 0-4 It's complex to explain and you have to know how the gravity code works to fully appreciate what this does, but basically the higher the number the more falling the sprite does.

Fourth chunk: Either 0 or 1, this lets a sprite change its horizontal movement without changing its left or right direction, say if it hits a wall. It's a bit more complex than this of course and rarely used.

Fifth chunk: Either 0 or 1, this lets a sprite change it's vertical movement without changing its up or down diretion, say if it is climbing up a hill. If this is 0 then a sprite can climb up hills, but not down them. Therefore *all* sprites that walk on the ground need this as 1.

Sixth chunk: Delay, or how long the sprite performs its current action for. Mostly this functions as an animation speed, but *everything* in the action is used until this time runs out. Max value is $8000, but that's way to long to bother with.

Seventh chunk: Horizontal movement. This is how much the sprite will move forward (Left or right depending where it's facing.) *after* the delay has elapsed. Many sprites use this to move, though it can get jerky. It does *not* change the h direction or vale and happens 'instantly' after each action.

Eigth chunk: Vertical movement. This is how much the sprite will move forward (Up or down depending where it's whether it's rising or falling.) *after* the delay has elapsed. Some sprites use this to move, though inot many. It does *not* change the h direction or vale and happens 'instantly' after each action.

Ninth chunk: Behavior. This is what a sprite does. Simple as that, how it moves and what actions it performs. Sprites don't *need* this, they can move about using the h and v movement, but controls everything else. It makes slugs slime for example. (Otherwise they would just crawl.) This chunk is four bytes long, an address, and a segment.

Tenth chunk: Sprite checking: What the sprite does when another sprite hits it. This includes stunning, killing Keen or stealing items. This chunk is four bytes long, an address, and a segment.

Eleventh chunk: Tile checking: What tiles a sprite looks for and what it does when it finds them. Mostly this lets sprites turn around when they come to the end of a piece of ground, or stop when they hit ceilings and floors. This basically stops sprites falling through everything. Nearly all sprites use this. Most are in segment G, but some, like the mushroom have special code mixed in with their behaviors and stuff.

Twelfth chunk: Next action. The next action to perform after the delay has elapsed. If this is nothing, then the sprite will freeze. Most sprites cycle around a certain number of actions, like Crawl_1 -> Crawl_2 -> Crawl_1...
lemm
Posts: 554
Joined: Sun Jul 05, 2009 12:32 pm

Dissecting the movement parameter

Post by lemm »

The "movement parameter" which ranges from 0-4 controls the timing of the think, or behaviour, function. Here's a summary of what each code means. Remember that the game is essentially a loop which is cycled through roughly 35 times a second. Also, when I refer to "action movement parameters," I am referring to the H/V anim move AMOUNTs.

Move Parm. Description.
0. Behaviour function is called ONLY when moving to a new action. The action movement parameters are used, but ONLY when the action is advanced. The delay timer is used to advance actions.
1. Behaviour function is called ONLY when moving to a new action. The action movement parameters are used EVERY FRAME, and the velocity is scaled by the number of tics consumed by this loop cycle. The delay timer is used to advance actions.
2. Behaviour function is called once every level loop. The movement parameters have no effect. The delay timer is NOT used, and action is advanced to the next action after one loop (thus, the behaviour function is only called once for this action).
3. Behaviour function is called once every level loop. The action movement parameters are used, but ONLY when the action is advanced. The delay timer is used to advance functions.
4. Behaviour function is called once every level loop. The action movement parameters are used EVERY FRAME, and the velocity is scaled by the number of tics consumed by this loop cycle. The delay timer is used to advance actions.

================

Thus we can group the movement parameters based on how they fit into three categories, with two possible options (0 or 1) per category:

A) Is the behaviour function called once every level loop (0) or only when the action advances (1) ?
B) Are the movement parameters used every level loop (0) or only when the action advances (1)?
C) Is the action advanced after one level loop (0) or timed by the action delay (1)?

Code: Select all

Movement Parm     Cat A.         Cat B.      Cat C.
0                   1             1             1
1                   1             0             1
2                   0             N/A           0
3                   0             1             1
4                   0             0             1

==================
User avatar
XkyRauh
Posts: 1114
Joined: Sun Aug 31, 2003 9:14 pm
Location: San Diego, California

Post by XkyRauh »

Can you give us an example of an in-game entity that makes use of each of the move parameters? That might help us understand more rapidly. :)
levellass
Posts: 3001
Joined: Wed Oct 11, 2006 12:03 pm
Location: Ngaruawahia New Zealand

Post by levellass »

Aah Lemm, you're right,t he name shouldn't be 'movement parameter', that's Adis' take on it. Possibly something like 'behavior control' or 'behavior call' anyone got a good name for it for Keenwiki? (Simply put it tells the sprite when to run its behavior code.)

Examples are:

0: Bulk of sprites. Call the behavior AND move when the sprite goes to do something else. (Roughly every time the sprite animates.) Poison slugs, Arachnuts, whatever. This is what makes poison slugs 'crawl' in jerks. If animation is often enough and speed slow enough it seems smooth, but isn't.

1: Some special situations. Just like above, but moement is different. Here we take the total movement and then divide it by how long it takes for the sprite to animate, the move smoothly. A sprite using this instead of 0 is exactly the same, but its movement is smoother. Berkeloids use this to make sure their 'floating' movement isn't jerky.

2: Ignore any movement and delay timer (Animation speed), run behavior all the time. Used notably for stunned falling sprites which keep falling all the time until they hit ground and move on. Airboards also use this. In other words, used for things that need to be instantly responsive. They won't animate and need movement defined in their behavior.

3: A mixture of 2.) and 0.); movement is again jerky, happening every time the sprite animates. This is for things that need to be very responsive, but also animate. Dopefish and Mad Mushrooms use this (They must be able to react to walls and floors to avoid getting stuck and move smoothly.)

4: A rarely used case. This is basically used for things that are going to need to land on or leave the ground somehow. (Usually) The flying bluebird does this for example, about the only K4 example.
lemm
Posts: 554
Joined: Sun Jul 05, 2009 12:32 pm

Post by lemm »

Lass posted good examples of the movement parameters. Here's my advice on how you would choose which movement parameter to use. Remember, the game is running at 70 tics per second, and each action is set to run for a certain number of tics (specified in the action timer field). Once that number of tics is reached, the sprite progresses to the "next" action.

I'll start with type 2. This is a special case that you wouldn't normally use to control locomotion of any sort, because the action is only called once and the movement parameters have no effect whatsoever. The best use for this type of action would be as an instantly responsive sensory action, as Lass suggested, or when you want to run a behaviour just once, like displaying a text box. If you want a "sensor" type action, you would set the "next action" field of the action to point to itself, creating an infinite loop until the sensor condition (checked in the behaviour function) breaks the loop and sets another action.

The remaining action types make use of the movement parameters, so they will be used for mobile sprites. We can classify the action types based on two categories as I remarked in the previous post.

The first category is motion. Will the sprite move in jerks, only being redrawn after the action timer expires, or will it be redrawn every animation frame (35 times a second), giving the appearance of smooth motion. If a sprite uses bipedal or serpentine locomotion, then you might want it to move in jerks. On the other hand, a fish or a space ship should move smoothly. Types 1 and 4 move smoothly, and types 0 and 3 move in jerks.

The second category is responsiveness, analogous to sprite type 2, meaning, "How often do we want to run the behaviours?" We can choose between running the behaviour once every animation frame (types 3 and 4), or once at the beginning of the action (types 0 and 1). If you want a sensor-type behaviour (search for keen, check if keen gets too close, check if keen turns his back), then you want instant responsiveness, so select action type 3 or 4. If you want to just run a behaviour once (throw a projectile, jump in the air), then select types 0 or 1.


-------------------------------------------------------------------------------------

Finally, a little on those two mysterious "change" parameters.

The "Change h" parameter is only every referenced once in the entire code. It seems to be a switch meaning "complete this action in one frame," but I could be wrong about that. I know it has something to do with how long an action takes to complete.


As for the change_v parameter:

Code: Select all

	if (o->action->change_v) 
	{
		if (o->topflags == TF_PLATFORM) 
		{
			KeenVel.y = 145;
		} 
		else 
		{
			if (KeenVel.x > 0) 
			   	KeenVel.y = KeenVel.x + 16;
			else
				KeenVel.y = -KeenVel.x + 16;
			notonplatform = true;
		}
	}
This tells the game that, for each frame, the sprite should move downwards by however far it moved horizontally, plus another pixel. In other words, the sprite should stick to the ground, as Lass has suggested on IRC. I've noticed that sprites with the change_v set to 1 will not fall properly.
lemm
Posts: 554
Joined: Sun Jul 05, 2009 12:32 pm

Post by lemm »

All of this information is now on the Keen Wiki, and a few corrections have been made, so refer to that from now on.
lemm
Posts: 554
Joined: Sun Jul 05, 2009 12:32 pm

Post by lemm »

Ahaha finally figured out the "Change H" parameter. It allows very fleeting actions (ie 0 delay actions) to not be drawn to the screen.
Bernie
Posts: 124
Joined: Sat Sep 22, 2012 2:28 pm
Location: Aus

Post by Bernie »

Okay i've asked this a bunch of times already in some threads and get nowhere- but could one of you kind souls please link me to a copy of a/the disassembly?? I'd love to poke around whats in there. pretty please??
Post Reply