-
Notifications
You must be signed in to change notification settings - Fork 37
Action Script
Action script, formerly known as Movement script and still abbreviated to movscr, is the language used (primarily) for controlling NPC and sprite movement in EarthBound. Its syntax is somewhat comparable to assembly, so it helps but is not necessary to have prior knowledge of that language.
To get started with making your own scripts, grab the three files here (yes there are three, but they display stacked).
This page will not cover all the functions you'll commonly be using, so it's a good idea to read through movscr_codes.ccs
as well to understand what you can and can't do.
For a sprite to show up, it'll need to have its direction and animation frame set. You can do this with m_set_facing_anim(dir, anim)
. dir
refers to the direction, 0-7 going clockwise starting at north. anim
controls the animation frame, either 0 or 1, which are the two frames next to each other in a sprite group. Also, to utilise 8-directional movement and graphics, you will need to m_set_var0(1)
. Enable a sprite's collision with m_enable_collision
, and use m_task_long(MovTask_DestroyIfFar)
for optimisation (we'll discuss tasks later).
These are the two main types of entity that you will be controlling, and that can have action scripts applied.
NPCs are what you place in EBProjEdit, they will persist on the map and will load whenever you get close to them. Also, they will have their dialogue when Talk To'd or Checked, no matter what their action script actually contains.
Sprites are entities generatable via CCScript, and are what you will probably use for cutscenes and the like. They are referenced by the sprite group ID they use. For this reason, you should avoid duplicate sprites on screen as a general rule, but it's not impossible to work with them. While an NPC's starting position is set in the map editor, a summoned sprite will need to manually set its position, or you can use a handful of other advanced function to anchor where they spawn from.
Let's create a simple script of an NPC who walks in place facing south.
walky:
m_enable_collision
m_task_long(MovTask_DestroyIfFar)
walky_loop:
m_set_facing_anim(4, 0)
m_pause(20)
m_set_facing_anim(4, 1)
m_pause(20)
m_jmp(walky_loop)
At the beginning we have two very important things. m_enable_collision
does just that. Sprites start with collision disabled, so don't forget to enable it if you plan for your player to interact with the sprite! m_task_long(MovTask_DestroyIfFar)
is also important for NPC-like scripts like this, as it will make sure the sprite is removed when it is off-screen to make the game run better.
Now, into our loop. The things happening here are pretty simple, the sprite will face 4
(south) and flip between its animation frames, at a delay of 20 frames. m_pause()
is very important, forgetting to include it will mean the sprite will do everything at once.
Finally, m_jmp
just goes back to the start of the loop. This is how you'll structure infinite loops in your scripts, which for NPCs and enemies are how you'll do the vast majority of your scripting to ensure that they don't stop moving.
We'll get to actual movement in a bit, but this script should give you a feel for the syntax of this language.
Now that you've made an action script, add it to movscr_reloc.ccs
with adr24(label)
. Then, you can use its new ID anywhere an action script (sometimes "movement pattern") ID is required.
Here are all the functions which move your objects:
-
m_set_xpos
,_ypos
(pos)
- Immediately moves the object to a location on that axis. Measured in pixels. -
m_add_xpos
,_ypos
(pos)
- Add this value to the object's position on that axis, also in pixels. Remember that Y is measured down from the top, so add a negative value to move upwards and a positive value to move downwards. -
m_warp_to_leader
- Immediately moves the object to the head of the party (or, center of screen). -
m_warp_to_pc
(pc)
- Immediately moves the object to this character in the party. (todo: char ID or party position?) -
m_warp_to_sprite
(sprite)
- Immediately moves the object to another object with this sprite group. -
m_warp_to_dest
- Immediately moves the object to its set destination (we will cover this later).
-
m_set_xvel
,_yvel
(vel)
- Sets the velocity on that axis to this value. The object will movevel/256
pixels every frame. -
m_add_xvel
,_yvel
(vel)
- Adds this value to the object's velocity on that axis. -
m_zerovel
- Sets velocities on all axis to 0.
-
m_set_speed
(spd)
- Sets the object's speed (for automated moving, see later commands) to this value. The object will movespeed/256
pixels every frame. -
m_move_until_at
(x, y)
- Makes the object walk to this location, measured in pixels. Uses the speed value. Also halts script execution until the object reaches its destination. -
m_start_walk
(dir)
- Starts the object walking in this direction. Uses the speed value.
(You also need to set the speed value for these).
-
m_set_dest_pos
(x, y)
- Sets the object's destination to this location, measured in pixels. -
m_set_dest_npc
(npc)
- Sets the object's destination to an NPC of this ID. -
m_set_dest_sprite
(sprite)
- Sets the object's destination to an object with this sprite group. -
m_set_dest_pc
(pc)
- Sets the object's destination to this party member. (todo: char ID or party position?) -
m_set_dest_leader
- Sets the object's destination to the party leader (or, centre of the screen). -
m_set_dest_party_tail
- Sets the object's destination to the tail of the party. -
m_set_dest_radius
(radius)
- Sets the radius which the object will use to check if it has arrived at its destination. For fast-moving objects, make sure to increase this value accordingly as to not overshoot the destination. Measured in pixels. -
m_set_default_dest_radius
- Sets the destination radius to the lowest possible value based on the movement speed. (CURRENTLY BROKEN, DO NOT USE) -
m_walk_to_dest
- Finally, makes the object walk to its set destination at its set speed, halting script execution until it is within the previously-set radius of arrival. -
m_walk_to_leader
- A macro which sets the destination to the party leader and then walks there.
Action scripts can run tasks, which are kind of sub-scripts that compliment the main script. They will always be run after the main script, in case that's ever important to what you write. Start a task with m_task_long()
or m_task()
if it's in the same file, and end them with m_endlasttask
from the main script or m_endtask
within the task itself. Some defines for common vanilla tasks below:
-
MovTask_Anim8
- Animate with 8 frame delay -
MovTask_Anim24
- Animate with 24 frame delay -
MovTask_Anim12
- Animate with 12 frame delay -
MovTask_Anim_Var4
- Animate with [VAR4] frame delay -
MovTask_Anim8_Toggle_DestroyIfFar
- Animate with 8 frame delay (togglable via VAR4) and destroy if far -
MovTask_Anim12_24_DestroyIfFar
- Animate with 12 then 24 frame delay and destroy if far -
MovTask_Anim24_DestroyIfFar
- Animate with 24 frame delay and destroy if far -
MovTask_Anim9_DestroyIfFar
- Animate with 9 frame delay and destroy if far -
MovTask_Anim6_DestroyIfFar
- Animate with 6 frame delay and destroy if far -
MovTask_Anim16_DestroyIfFar
- Animate with 16 frame delay and destroy if far -
MovTask_HandleCollision
- Handle collisions. Doesn't need to be used by stationary entities -
MovTask_DestroyIfFar
- Destroy if far -
MovTask_EnemyTouch1
- Start battle with enemy on touch -
MovTask_EnemyTouch2
- Very similar to above, I don't actually know what's different -
MovTask_PartyLook
- Party members look at ENTITY -
MovTask_DestroyIfFar_UnsetFlag_10
- Destroy if far and unset event flag 10 -
MovTask_ButterflyTouch
- Butterfly effect on touch -
MovTask_CallNpcScriptOnTouch
- Call NPC primary text script on touch
These are especially important to use when writing NPC and enemy action scripts. You can also write your own.
Each action script has a 16-bit main register (RESULT) which is used to pass arguments to functions and do math, and eight 16-bit variables (VAR) labelled 0-7 which can be freely used to write values to and read values from. The variables are also sometimes used for other things:
- Enables 8-directional graphics when set to
1
- -
- -
- -
- Controls animation speed for
MovTask_Anim_Var4
and toggle forMovTask_Anim8_Toggle_DestroyIfFar
- Destination detection radius
- X of destination
- Y of destination
If you aren't using the functions that look at these variables, don't worry about using them. You can also read and write to RAM with m_get_mem16(address)
, m_set_mem8(address)
, and m_set_mem16(address)
.
Also, RESULT will pass to A when using m_asmcall
, and A will be returned in RESULT when an m_asmcall
's function ends.
Without ASM, the camera is locked to the party leader always, but there are a few control codes to anchor the party leader to an entity, which you can then move around.
-
[1F EE XX XX]
Focus on an NPC -
[1F EF XX XX]
Focus on a sprite -
[1F ED]
End focus
You will also need to use [1F E8 FF]
to unfreeze the party if you want the movement to work. Don't worry, the player can't move them around with the controller. You can use this in combination with hiding the party for things like the bus cutscenes, or use it to make elaborate cutscenes like the Runaway Five's performances.
- Overworld Sprites
- Battle Backgrounds
- Battle Sprites
- Title Screen
- Window Graphics
- Logos
- Fonts
- Animations
- Swirls
- EB Project Editor
- Tile Data
- Tile Editor
- Collision Data
- Adding Map Palettes
- Map Editor
- Doors
- Warp Styles
- Enemy Placement
- Hotspots