This page is about sprite actions in Keen Galaxy and Dreams. Unlike in Keen Vorticons, Galaxy and Dreams splits sprites into three main components; code, parameters and actions.
Code behaves much as the entirety of a sprite does in Vorticons. It contains most of the decisions a sprite must make such as what to do when faced with a wall, hit by a shot, etc.
Parameters are also present in Vorticons and behave about the same as well. They are values stored in memory in a sprite structure. So, for example, while a sprite's animation may be changed in code or actions the current animation a single sprite in-level is using at any given instant in time is controlled by a sprite parameter.
Actions, finally, define simpler things; such as animations to use, most speeds and pointers to code. (Allowing sprites to use not just one piece of code but several.) In a real sense actions provide a 'scaffolding' onto which sprite code it arrayed.
Actions can be treated like text strings, with pointers to them. They are located in the text segment of the executable, usually in 'chunks' of multiple actions.
Action Structure and Integers
Galaxy action integers (or 'ints') are integral fields within actions that set various attributes of the sprite such as the animation it uses or its speed when the sprite first uses that action. (Again this is not to be confused with sprite parameters, which are the values a sprite is currently using and which are altered by code and actions.)
Actions are 30 bytes long and consist of various two- and four-byte value. A breakdown of these values is as follows:
Int Description Values Int 0: Left sprite Valid sprite chunks Int 2: Right sprite Valid sprite chunks Int 4: Type 0-4 Int 6: Deprotect Animation 0/1 Int 8: Stick to Ground 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 Int 18: Behavior segment Segment values Int 20: Check sprites Start of check sprite codes Int 22: Check segment Segment values only Int 24: Check tiles Start of check tile code Int 26: Check segment Segment values Int 28: Next action Valid actions
Following is a more complete, but still brief, description of the various parameters. More information can be gained from each value's page: Patch:Sprite animations, Patch:Sprite behavior, Patch:Sprite collision, Patch:Sprite tile check, Patch:Galaxy Action Parameters.
The action type defines how often the the behavior function is called and how often the action movement amount offsets are applied. It takes five meaningful values (0-4) summarized below:
Movement Parm Behavior Called... Action Movements Applied... Is Delay field used? 0 Once Once Yes 1 Once Every frame Yes 2 Every frame Not applied No 3 Every frame Once Yes 4 Every frame Every frame Yes
The behavior is called either once, right before the action changes to the next action, or on every animation frame. The action movements are applied to the object either once, when the action changes, or on every animation frame, scaled by the number of tics consumed by one animation frame (usually 2 tics/frame at optimum speed). The delay field is used, meaning it specifies the number of tics before the action is advanced to the next action, or it is unused, meaning the action repeats itself every frame.
In general, a behavior that is called once would be used for spawning something (e.g., a slime puddle or projectile), while a behavior called every frame would be used as a sensor. Movements applied once per action are used for jerky locomotion (pedal, or serpentine), whereas movements application every frame is used for smooth motion. Movement can also be defined in the behavior, collision, and tile checking functions, in addition to the action movement parameters. Examples of where action parameters are found, and how they can be used are described below.
The bulk of sprites use type 0, meaning that the behavior is called AND the object is moved when action advances ( (i.e., when the sprite animates.) This is what makes poison slugs or Arachnuts, for example, 'crawl' in jerks. If animation is often enough and speed slow enough, motion seems smooth.
Type 1 is similar to type 0, but movement is smoother. Here, the H/V movement offsets are applied every frame, depending on which way the Berkeloid is facing. Berkeloids use this to make sure their 'floating' movement isn't jerky.
Type 2 actions ignore any movement and delay timers (animation speed). It is used notably for stunned falling sprites which keep falling all the time until they hit ground and move on. Moving Platforms 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. Type 2 is best used for sensory behaviors. Type 2 actions are executed once, and only once, per animation frame.
Movement is 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 because they must be able to react to walls and floors to avoid getting stuck. The behavior function of these creatures defines the motion, which is why they do not move in jerks.
Type 4 is a rarely used case, sometimes used for things that are going to need to land on or leave the ground somehow. The flying bluebird does this for example, and it is about the only Keen 4 example.
The rarely-used, boolean "Deprotect Animation" parameter forces the action to run for the proper number of tics, even if this means skipping over an animation. If this flag is not set, the sprites for actions will be drawn for at least one animation frame, no matter how short the delay is (even a delay of 0 or 1 tics). If the flag is set, then these fleeting actions will still have their sprite behavior function and movement parameters processed (but not the sprite collision nor tile-checking functions), but the animation MIGHT not be drawn (see the detailed explanation). This flag is most useful if you want to apply the action movement parameters or a behavior instantaneously (using 0 for the delay), and you don't want the intermediate sprite to be seen, or you don't want a hiccup in the animation.
For example, you might want object A to call a single behavior six times in a row by writing six sequential type 0 actions of delay 0. This behavior might spawn object B at object A's location. If the animation is protected (flag = 0), then this will take a minimum of six animation frames, and object A will be drawn every frame. If the animation is deprotected (flag = 1), then all sprites will spawn on the same animation frame. In addition, the movement parameters can be tweaked so that object A is located at a different position for each object B spawn, and returns to its initial position after the sixth spawn. If animation is protected, then object A will skip around the screen, whereas if it is not protected, its intermediate positions will not be drawn.
This parameter has no meaning for type 2 actions.
Detailed explanation: For any animation frame, when an action expires because it reaches its delay, the game runs the old action for a fraction of the frame length, and the new action for the remainder of the frame. As an example, assume that action 1 has 3 more tics to go, and the animation frame took 5 tics to complete. Then action 3 would be run for 3 tics and action 2 would be run for 2 tics. In the event that the second action is so short that there are still tics to be processed, the "Deprotect Animation" flag comes into use. If reset (zero), then the action is run for delay - 1 tics and no more actions are processed for that frame. If the "Deprotect Animation" flag is set (1), then the action is run for the specified number of tics and the 3rd action in the chain is processed for the remaining number of tics. Since the 3rd action is the last to be processed, its frame will be displayed, and the sprite frame for action 2 is never seen.
Stick to Ground
The boolean "Stick to Ground" (formerly, "Change V") parameter affects the vertical movement of the object. If Stick to Ground is 1, then the sprite will move DOWNWARDS by however far it moved left or right, for each animation frame. Some objects will malfunction if they leave the ground (e.g., inchworms and Arachnuts). Because slopes are always less than 45 degrees, objects will always be forced into the ground if they are moving downhill. If an object is to walk along the ground, this flag should be set to 1. If the object is airborne, the flag must be set to 0.
Delay and Next Action
The "Delay" field specifies how many tics will pass before the action pointed to by the "Next action" pointer is selected. If an action has a delay of 70, that action will remain valid for one second until the next action is selected. Both fields serve no purpose for action type 2. For movement types 0, 1, and 3, the delay also regulates how often the behaviour function or the movement parameters are applied (see table above). If the next action is zero, then the sprite freezes.
H/V Movement Amount
The movement offsets allow for action-defined motion. Movement offsets (here, and in general) are always in 16ths of a pixel, or 256ths of a tile. Objects have an X-Motion (-1 = left, 0 = none, 1 = right), and a Y-motion parameter (-1 = up, 0 = none, 1 = down) that is defined by the sprite behavior, collision, and tile-checking functions. For action types 0 and 3, the H and V values are applied when the action delay timer expires. For example, an action of parameter type 0, H-offset of 128, and delay of 35 will move right or left by a half tile every half second. For action types 1 and 4, the H and V values are applied every animation frame, and scaled by the number of tics consumed during that frame (approximately 2 tics/frame on DOSBox), meaning that the action delay is irrelevant for motion and that the motion is smooth. An object with action type 1 or 4 and a V-offset of 4 will move 1/4 (four 16ths) of a pixel every game tic, which is equal to 1/2 a pixel every game frame and about 17 pixels a second.
Movement can also be defined in the behavior, collision, or tile-check functions. The offset applied in these functions is added to that defined by the action, and the net movement is applied to the object. Remember that, if the Stick-to-Ground flag is set, the vertical movement defined by either the action or a function is ignored.
Behavior, Collision, and Tile Checking Functions
These are double-word pointers to the start of a function described in detail elsewhere. Note that the behavior function is called every frame for action types 1,2, and 4, but it is called right when the action switches to the next action for types 0 and 3. The collision function is called every frame whenever an object is in contact with another object, and the tile-collision (draw function) is called every frame when the object is on screen.
Differences in Other Games
Although not part of the "Id Engine" proper, the physics and action code is shared by many games that use the Keen:Galaxy engine. BioMenace, Dangerous Dave 3 and 4, and Rescue Rover 2 all use actions.
Shadow Nights, Dangerous Dave 2, and Rescue Rover 1 use the Keen Vorticons method of sprite control, being entirely code-based.
While Biomenace uses the same engine as Keen actions in it are 32 bytes long; an extra two bytes exists before the next action value.) Action strings are also located only at addresses divisible by $10 ($22F30, $22F40...) This allows the Next Action value to work differently than in Keen. The value is the actual address of the next action minus some value, divided by $10. As an example, to point to an action at $22F30 the value $2A06 is used in the freeware Biomenace 1. This is ($22F30 + $7110) / $10.
Int Description Values Int 0: Left sprite Valid sprite chunks ... ... ... ... Int 24: Check tiles Start of check tile code Int 26: Check segment Segment values Int 28: PADDING Zero Int 30: Next action Valid actions
Keen Dreams uses an earlier, more primitive version of the action format. This is largely the same.