Complete SCD Scripting Guide ‐ Resident Evil 2 - biorand/biohazard-utils GitHub Wiki
SCD - Resident Evil 2
In Resident Evil 1-3, each room is stored a file (.RDT). One of the sections of the file is known as the .SCD which is a game script that will run while in the room. The script will setup all the doors, enemies, items, events and cutscenes that happen within that room.
Tables
Each room has several tables corresponding to different things within the room. Area of interests (AOTs), and enemies can be set up freely in the SCD. Messages must be setup up in the MSG section of the RDT, and object models in the object/texture section of the RDT. The SCD compiler allows you to edit these via preprocessor directives.
These tables are a fixed size, thus you can only have a limited number of each kind.
Table | Instructions |
---|---|
AOT (area of interest) | aot_set , aot_reset , aot_set_4p , door_aot_se , door_aot_set_4p , item_aot_set , item_aot_set_4p , aot_on |
EM (enemy / npc) | sce_em_set |
MSG (message) | message_on |
OM (object) | obj_model_set |
Many instructions can be used to change the properties or behaviour of AOTs, EMs, or OMs. But rather than passing the table type and ID to each instruction, work_set
is called which changes the current entity. Then any instruction following that will affect only the entity that was passed to work_set
. For example:
work_set(WK_ENEMY, ID_EM_1);
pos_set(0, -21000, 0, -25000);
dir_set(0, 0, 1024, 0);
member_copy(V_TEMP, M_TYPE);
calc(0, OP_AND, V_TEMP, 0x7FFF);
member_set2(M_TYPE, V_TEMP);
A common pattern for enemies that walk into a room is to initialise the enemy in a frozen state out of bounds (-32000, -10000, -32000) where you can't see it. Then when the event for it walking in is triggered, this code snippet will set the current entity to work on, to the second enemy slot (EM 1), and then move the enemy to a certain position and orientation. It also unfreezes the enemy which is done by switching off a flag using a bit mask in the type member of the enemy.
AOTs (area of interest)
Area of interests are invisible rectangular areas within the room. The areas can be triggered by either the player entering the area, or the player using the action button within the area or facing the area. The trigger is controlled by a set of flags. The effect can either be a door/room change, an item pick up, a message, or running a subroutine. It is also used to mark areas where a key item can be used.
EM (enemies / npcs)
Enemies can be an enemy, NPC or partner. Partners use ID 255, and is reserved for Ada and Sherry usually resulting in their specific partner AI behaviour.
OM (objects)
Objects can be any 3D model that appears in the room. Some can be movable items such as statues or boxes, or the model of an item to pickup.
Messages
Question types
- 0x00 - YES / NO
- 0x01 - LEFT / RIGHT / NO
- 0x02 - 1 / 2 / 3 / 4 / 5 / 6
- 0x03 - UP / DOWN
- 0x04 - LEFT / RIGHT / CANCEL
- 0x05 - YES / NO / ORIGINAL
- 0x06 - MG / SIDEPACK
To get the user's answer, you can assume the following flags as the bits that make up the answer index.
ck(FG_MESSAGE, 29, 1)
(4)ck(FG_MESSAGE, 30, 1)
(2)ck(FG_MESSAGE, 31, 1)
(1)
For example to check if 3 was chosen, you would do:
if (ck(FG_MESSAGE, 29, 0)
ck(FG_MESSAGE, 30, 1)
ck(FG_MESSAGE, 31, 1))
{
// 3 was chosen
}
because 3 is made up of 1 + 2, so you check 1 and 2 are set, and that 4 is not set.
Instructions
0x00: nop
A single byte instruction that does nothing. Useful for alignment, and removing instructions without alterating the address of others.
Example:
1C94: evt_next
1C95: nop
1C96: set FG_ROOM, 0, 0
Here a nop instruction is used to align the set instruction to an even address, due to evt_next
only being one byte long. When writing a .bio file, nop instructions are not required as the compiler will insert them automatically when required.
0x01: evt_end(u8 padding)
Returns from the currently executing subroutine.
Example:
proc init
{
door_aot_se(ID_AOT_0, SCE_DOOR, SAT_PL | SAT_MANUAL | SAT_FRONT, 0, 0, -23889, -26379, 1800, 2200, -26505, 0, -24896, 0, 0, 9, 6, 0, 0, 0, 0, 0, UNLOCKED, 0);
if (ck(FG_STATUS, F_BONUS, 1))
{
sce_em_set(0, ID_EM_0, ENEMY_ZOMBIE_RANDOM, 6, AI_DEFAULT, 0, SBK_6, 0, 74, -24336, 0, -25994, 2859, 0, 0);
sce_em_set(0, ID_EM_1, ENEMY_ZOMBIE_RANDOM, 0, AI_DEFAULT, 0, SBK_43, 0, 123, -23892, 0, -22831, 3125, 0, 0);
evt_end(0);
}
sce_em_set(0, ID_EM_0, ENEMY_LICKER_RED, 1, AI_01, 0, SBK_14, 0, 76, -27000, -3000, -12400, 1024, 0, 0);
sce_em_set(0, ID_EM_1, ENEMY_LICKER_RED, 0, AI_DEFAULT, 0, SBK_14, 0, 77, -22712, 0, -6235, 1800, 0, 0);
}
In this example, evt_end
is used to terminate the initialization event after setting up the doors and enemies for 4th survivor. Avoids needing an else block for the remainder of the procedure.
If not writing in a .bio file, you must also end each subroutine with an evt_end
instruction:
.proc init
1FDE: door_aot_se ID_AOT_0, SCE_DOOR, SAT_PL | SAT_MANUAL | SAT_FRONT, 0, 0, -19547, -23166, 1600, 2300, -25979, -14400, -21180, 3920, 0, 23, 3, 8, 13, 1, 0, 0, UNLOCKED, 0
1FFE: door_aot_se ID_AOT_1, SCE_DOOR, SAT_AUTO, 0, 0, 0, 0, 0, 0, 1425, 0, -21914, 3072, 1, 25, 1, 0, 51, 8, 0, 0, UNLOCKED, 0
201E: evt_end 0
0x02: evt_next
Suspends the current event until the next frame. This can be used instead of sleep(10, 1)
to let the game advance one frame before continuing the subroutine. It is used occasionally for timing, but is used more often to create a loop that waits for a condition to be met without freezing the game.
Examples:
while (ck(FG_ROOM, 0, 0))
{
evt_next();
}
Here, evt_next
is used to let the game advance one frame before checking the condition again. Without it, the game would freeze as the loop would never end, and the game would never get the chance to run any other code.
work_set(WK_OBJECT, ID_OBJ_0);
speed_set(0, -150);
repeat (20)
{
add_speed();
evt_next();
}
In this example, an object is given a speed to move at and is moved at that speed for 20 frames.
0x03: evt_chain
Not certain yet how this instruction works. It possibly is used to run another event when the current one ends.
0x04: evt_exec(u8 evt_id, u8[] instruction)
Begins a new event and executes the following instruction. The instruction is always typically a gosub
which makes evt_exec
a four byte instruction. evt_exec
is used to have multiple subroutines running simultaneously which is useful for cutscenes where multiple things are happening.
evt_id
- used to specify the event slot that will be used for the new event. Usually 255 is specified which means that the event will automatically be given a spare event slot. If however you need to terminate the event prematurely, you can give the event an ID that you later pass toevt_kill
.instruction
- the instruction to execute in a new event. Usuallygosub
.
Example:
.proc main
evt_exec 255, I_GOSUB, leon
evt_exec 255, I_GOSUB, claire
evt_end 0
.proc leon
sleep 10, 10
work_set WK_PLAYER, 0
plc_neck 2, 0, -512, 0, 67, 67
evt_end 0
.proc claire
sleep 10, 30
work_set WK_ENEMY, ID_EM_0
plc_neck 0, 0, 512, 0, 55, 55
evt_end 0
Here we kick off two new events that will run independently. One for controlling the player model, and another to control the NPC.
In a .bio file, you can use the fork
statement:
proc main
{
fork leon;
fork {
sleep(10, 30);
work_set(WK_ENEMY, ID_EM_0);
plc_neck(0, 0, 512, 0, 55, 55);
}
}
proc leon
{
sleep(10, 10);
work_set(WK_PLAYER, 0);
plc_neck(2, 0, -512, 0, 67, 67);
}
You can pass a procedure name to fork, or an inline anonymous procedure block as shown above.
0x05: evt_kill(u8 evt_id)
Used to terminate a running event created using evt_exec
.
Example:
proc main
{
evt_exec(9, I_GOSUB, shake_head);
sleep(10, 300);
evt_kill(9);
}
proc shake_head
{
repeat {
work_set(WK_PLAYER, 0);
plc_neck(2, 0, -512, 0, 67, 67);
sleep(10, 60);
plc_neck(2, 0, 512, 0, 67, 67);
sleep(10, 60);
}
}
A new event (id 9) is created that repeated moves the player's head forever. But after 300 frames (10 seconds), the event is terminated using evt_kill
.
0x2E: work_set(u8 kind, u8 id)
kind
- The entity kind.- 0x00:
WK_NONE
- Presumably clears the current entity. Can't see any reason to use this. - 0x01:
WK_PLAYER
- The player model. - 0x02:
WK_SPLAYER
- The partner model set usingsce_em_set
and ID 255 (Ada / Sherry). - 0x03:
WK_ENEMY
- An enemy set usingsce_em_set
(do not use for the partner EM 255) - 0x04:
WK_OBJECT
- An object set usingobj_model_set
. - 0x05:
WK_DOOR
- A door set usingdoor_aot_se
, ordoor_aot_set_4p
. - 0x06:
WK_ALL
- Not sure what this for.
- 0x00:
id
- The slot index if applicable.
Example:
work_set(WK_PLAYER, 0);
pos_set(0, -21000, 0, -25000);
dir_set(0, 0, 1024, 0);
Sets the current entity to work on to the player model, then sets the position and direction of it.
0x3F: plc_motion(u8 group, u8 animation, u8 flags)
group
- The animation group.- 0 - Relevant EDD embedded in the RDT.
- 1 - First EDD in PLD/EMD file.
- 2 - Second EDD in PLD/EMD file.
mode
- Flags that control how the animation is played.- 0x01 - Seems to apply some movement once
- 0x02 - Seems to apply some movement once
- 0x04 - Loop
- 0x08 - x2 speed
- 0x10 - x0.5 speed
- 0x80 - Reversed
Example:
work_set(WK_PLAYER, 0);
plc_motion(1, 6, 0);
Causes the player model to play the crouching animation once (animation 6 from the first EDD of the PLD file).
0x44: sce_em_set(u8 padding, u8 id, u8 type, u8 pose, u8 behaviour, u8 floor, u8 sound, u8 texture, u8 globalId, s16 x, s16 y, s16 z, s16 d, u16 animation, u16 padding)
Initialises an enemy or NPC in the room. For the most part, this instruction alone is enough for an enemy, but any special behaviour or event requires further instructions that act on the enemy ID. This must be called on the first frame when loading the room, so do not use after any evt_next
, sleep
instructions or in a subroutine called from an AOT or evt_exec
. It is good practice to initialise all your NPCs first, then enemies at the start of your script to avoid potential issues. You can have different sce_em_set
for the same ID, but make sure they only run independently on a room load and not both together.
id
- The EM slot ID. Each enemy / npc must have a unique ID in the room. 255 should be used for the partner.type
- The enemy type.
Enemies | NPCs |
---|---|
0x10: ENEMY_ZOMBIE_COP |
0x40: ENEMY_CHIEF_IRONS_1 |
0x11: ENEMY_ZOMBIE_BRAD |
0x41: ENEMY_ADA_WONG_1 |
0x12: ENEMY_ZOMBIE_GUY_1 |
0x42: ENEMY_CHIEF_IRONS_2 |
0x13: ENEMY_ZOMBIE_GIRL |
0x43: ENEMY_ADA_WONG_2 |
0x14: ENEMY_ZOMBIE_TEST_SUBJECT |
0x44: ENEMY_BEN_BERTOLUCCI_1 |
0x15: ENEMY_ZOMBIE_SCIENTIST |
0x45: ENEMY_SHERRY_PENDANT |
0x16: ENEMY_ZOMBIE_NAKED |
0x46: ENEMY_BEN_BERTOLUCCI_2 |
0x17: ENEMY_ZOMBIE_GUY_2 |
0x47: ENEMY_ANNETTE_BIRKIN_1 |
0x1E: ENEMY_ZOMBIE_GUY_3 |
0x48: ENEMY_ROBERT_KENDO |
0x1F: ENEMY_ZOMBIE_RANDOM |
0x49: ENEMY_ANNETTE_BIRKIN_2 |
0x20: ENEMY_ZOMBIE_DOG |
0x4A: ENEMY_MARVIN_BRANAGH |
0x21: ENEMY_CROW |
0x4B: ENEMY_MAYORS_DAUGHTER |
0x22: ENEMY_LICKER_RED |
0x4F: ENEMY_SHERRY_JACKET |
0x23: ENEMY_ALLIGATOR |
0x50: ENEMY_LEON_KENNEDY_RPD |
0x24: ENEMY_LICKER_GREY |
0x51: ENEMY_CLAIRE_REDFIELD |
0x25: ENEMY_SPIDER |
0x54: ENEMY_LEON_KENNEDY_BANDAGED |
0x26: ENEMY_BABY_SPIDER |
0x55: ENEMY_CLAIRE_REDFIELD_NO_JACKET |
0x27: ENEMY_G_EMBRYO |
0x58: ENEMY_LEON_KENNEDY_CAP_TANK_TOP |
0x28: ENEMY_G_ADULT |
0x59: ENEMY_CLAIRE_REDFIELD_COW_GIRL |
0x29: ENEMY_COCKROACH |
0x5A: ENEMY_LEON_KENNEDY_BLACK_LEATHER |
0x2A: ENEMY_TYRANT_1 |
|
0x2B: ENEMY_TYRANT_2 |
|
0x2D: ENEMY_ZOMBIE_ARMS |
|
0x2E: ENEMY_IVY |
|
0x2F: ENEMY_VINES |
|
0x30: ENEMY_BIRKIN_1 |
|
0x31: ENEMY_BIRKIN_2 |
|
0x32: ENEMY_BIRKIN_3 |
|
0x33: ENEMY_BIRKIN_4 |
|
0x34: ENEMY_BIRKIN_5 |
|
0x39: ENEMY_IVY_PURPLE |
|
0x3A: ENEMY_GIANT_MOTH |
|
0x3B: ENEMY_MAGGOTS |
-
pose
- Known as the enemy pose, but is often the initial state of the enemy. Each enemy has different poses.- 0x00:
POSE_ZOMBIE_WAIT
- 0x01:
POSE_ZOMBIE_LYING
- 0x02:
POSE_ZOMBIE_WAKE_UP
- 0x03:
POSE_ZOMBIE_CRAWL
- 0x04:
POSE_ZOMBIE_GET_UP
- 0x05:
POSE_ZOMBIE_DEAD_UP
- 0x06:
POSE_ZOMBIE_FOLLOW
- 0x07:
POSE_ZOMBIE_DEAD
- 0x08:
POSE_ZOMBIE_EATING
- 0x40:
POSE_ZOMBIE_40
- 0x00:
POSE_DOG_IDLE
- 0x02:
POSE_DOG_HOSTILE
- 0x05:
POSE_DOG_EATING
- 0x00:
-
behaviour
- Common flags that controls the enemy AI. Use bitwise-or to combine them.- 0x01:
AI_01
- - 0x02:
AI_02
- - 0x04:
AI_04
- - 0x08:
AI_08
- - 0x10:
AI_10
- - 0x20:
AI_20
- - 0x40:
AI_40
- For some enemies, this flag will ignore the player until hurt. Useful for e.g. zombies that are eating a dead body. - 0x80:
AI_INACTIVE
- The enemy will be frozen. Use this for sleeping zombies or enemies not yet in the room.
- 0x01:
-
floor
- The initial floor the enemy is on. This is usuallyY / -1800
. -
sound
- The sound ID. Each enemy has a specific sound ID, but in rooms where multiple types of enemies exist, special sound IDs are required which contain all the required sounds. Only one enemy sound file can be loaded for a room. Using multiple IDs that require different files will result in silent enemies. -
globalId
- Each enemy in the game requires a unique global ID which is used to track which enemies you have killed or not. Sharing an ID would mean that killing one would prevent all others with the same global ID from spawning. 255 is a special ID that means that an enemy will always respawn. This means you can have 254 unique enemies, however the game uses a different group of flags for stages 1-4 and 5-7 which therefore gives you 508 across the whole game. If you require more, then you will need to selectively reset the enemy IDs after an event or point-of-no-return. Whether an enemy is killed or not can be checked withck(FG_ENEMY, globalId, 1)
(stage 1-4) orck(FG_ENEMY_2, globalId, 1)
(stage 5-7). You can reset the killed status usingset(FG_ENEMY, globalId, 0)
. -
texture
- Usually 0, but some zombies can have this set to change their body texture for zombies that have multiple body textures in their file. -
x
- Initial position in the room on the X-axis. -32000 is usually used for out of bounds (not yet spawned in room). -
y
- Initial position in the room on the Y-axis. -10000 is usually used for out of bounds (not yet spawned in room). -
z
- Initial position in the room on the Z-axis. -32000 is usually used for out of bounds (not yet spawned in room). -
d
- Initial orientation of the enemy between 0 to 4096, or -2048 to +2048. 1024 is 90 degrees. -
animation
- Unsure what this is for, leave at 0.
Examples:
if (ck(FG_COMMON, 20, 0)) {
sce_em_set(0, ID_EM_0, ENEMY_ZOMBIE_GUY1, 0, AI_DEFAULT, 0, SBK_5, 0, 66, -26174, 0, -178, 1208, 0, 0);
sce_em_set(0, ID_EM_1, ENEMY_ZOMBIE_GIRL, 0, AI_DEFAULT, 0, SBK_10, 0, 67, -23477, 0, 2299, 920, 0, 0);
} else {
sce_em_set(0, ID_EM_0, ENEMY_ZOMBIE_DOG, 0, AI_DEFAULT, 0, SBK_12, 0, 135, -17564, 0, -11339, 3053, 0, 0);
sce_em_set(0, ID_EM_1, ENEMY_ZOMBIE_DOG, 0, AI_DEFAULT, 0, SBK_12, 0, 136, -23651, 0, -23105, 2421, 0, 0);
sce_em_set(0, ID_EM_2, ENEMY_ZOMBIE_DOG, 0, AI_DEFAULT, 0, SBK_12, 0, 138, -22376, 0, -25015, 3381, 0, 0);
}
This example spawns two zombies, unless flag 20 is set, in which case three dogs spawn instead. Notice how the same enemy IDs, and incompatible sound IDs can be used because the instructions are mutually exclusive.
0x5B: plc_cnt(u8 frames)
Skips the specified number of frames in the currently playing animation started with plc_motion
.
frames
- The number of frames to skip.
Example:
work_set(WK_PLAYER, 0);
plc_motion(1, 2, 0);
plc_cnt(94);
Causes the player model to play the dying animation once starting from frame 94. The game will morph the player's model gradually from the current pose to frame 94 of the dying animation and then play the rest of the animation to the end.
0x51: sce_bgm_control(u8 channel, u8 operand1, u8 operand2, u8 left, u8 right)
Plays, stops, or adjusts the volume and panning of background music, or ambient sound effects. The game internally stores the loaded music in a channel which means that when switching rooms, if the room contains the same BGM track for the channel, it will continue playing and not restart from the beginning. This prevents the music repeatedly restarting when a whole area contains the same BGM.
channel
- The BGM channel to change.- 0x00:
BGM_CHANNEL_MAIN
- Usually used for main background music. - 0x01:
BGM_CHANNEL_SUB0
- Usually used for event / cutscene background music. - 0x02:
BGM_CHANNEL_SUB1
- Usually used for ambient sound effects.
- 0x00:
operand1
- The primary operation to perform for the channel.- 0x00:
BGM_OP_NOP
- Performs no operation. - 0x01:
BGM_OP_START
- Starts playing the channel. - 0x02:
BGM_OP_STOP
- Stops playing the channel. - 0x03:
BGM_OP_RESTART
- Seems to behave the same asBGM_OP_START
. I can't find a way of actually restarting a track. - 0x04:
BGM_OP_PAUSE
- Seems to behave the same asBGM_OP_STOP
. - 0x05:
BGM_OP_FADEOUT
- LikeBGM_OP_STOP
but fades out the BGM gradually.
- 0x00:
operand2
- A secondary operation to perform.- 0x00:
BGM_TYPE_MAIN_VOL
- Controls the volume of the main track. - 0x01:
BGM_TYPE_PROG0_VOL
- Controls the volume of the secondary track 0. - 0x02:
BGM_TYPE_PROG1_VOL
- Controls the volume of the secondary track 1. - 0x03:
BGM_TYPE_PROG2_VOL
- Controls the volume of the secondary track 2.
- 0x00:
left
- The left speaker volume. Tends to be between 0 and 120. 64 is normal volume.right
- The right speaker volume. Tends to be between 0 and 120. 64 is normal volume.
Example 1:
proc main
{
if (ck(FG_COMMON, 12, 0)) {
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_START, 0, 0, 0)
sce_bgm_control(BGM_CHANNEL_SUB0, BGM_OP_STOP, 0, 0, 0)
} else {
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_STOP, 0, 0, 0)
sce_bgm_control(BGM_CHANNEL_SUB0, BGM_OP_START, 0, 0, 0)
}
}
Plays the main BGM if flag 12 is not set, otherwise plays the secondary BGM.
Example 2:
proc bgm_loop
{
repeat {
switch (V_CUT) {
case 0:
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG0_VOL, 1, 65);
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG1_VOL, 61, 65);
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG2_VOL, 51, 65);
break;
case 1:
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG0_VOL, 1, 65);
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG1_VOL, 61, 65);
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG2_VOL, 51, 65);
break;
case 2:
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG0_VOL, 1, 65);
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG1_VOL, 61, 65);
sce_bgm_control(BGM_CHANNEL_MAIN, BGM_OP_NOP, BGM_TYPE_PROG2_VOL, 51, 65);
break;
}
evt_next();
}
}
Runs a continuous loop to adjust the ambient background music volume and panning depending on the camera angle.
0x57: sce_bgmtbl_set(u8 padding, u8 room, u8 stage, u16 id, u16 padding)
Sets the primary and secondary background music tracks for a given room. The game internally keeps a table of every room and the background music that should be loaded for it. This table is initialised when starting a new game using INIT_TBL.DAT
. This instruction can update that table. Unfortunately this cannot update the BGM in realtime, thus if you update the table for the current room the player is in, it will not take effect until the player reloads the room.
Mods can either update INIT_TBL.DAT
to initialise each room's BGM, or update all the required table entries in the very first room of the game.
room
- The room number, e.g. 0x01 for Kendo's shop.stage
- The stage number, e.g. 0x00 for city / 2F RPD.id
- The file ID for the main channel and the secondary channel. 0xFF means no BGM / silent.
Example:
sce_bgmtbl_set(0, 0x01, 0x00, 0x3302, 0);
This sets ROOM1010 (Kendo's shop) to use 0x02: MAIN02.SAP
(Library) for the main channel, and 0x33: SUB42.SAP
(Brad) for the secondary channel.
Most of the BGM IDs correspond to the .sap filenames, but some like 0x33 do not.
The following macro may be useful:
#define SET_BGM(room, bgm, mus) sce_bgmtbl_set(0, room & 0xFF, (room >> 8) & 0xFF, bgm | mus, 0)
Which can be used as follows:
#define BGM_LIBRARY 0x0002
#define BGM_BASEMENT 0x0003
#define BGM_RPD_HALL 0x0005
#define BGM_NONE 0x00FF
#define MUS_ZOMBIE_ATTACK 0x0000
#define MUS_HAUNT 0x3300
#define MUS_NONE 0xFF00
proc main
{
// Set room 200 (main hall) to library BGM, no secondary BGM
SET_BGM(0x200, BGM_LIBRARY, MUS_NONE);
}
0x60: kage_set(3, u8 em_id, u8 red, u8 green, u8 blue, s16 x_size, s16 z_size, s16 x_offset, s16 z_offset)
Adds a coloured shadow under the enemy/npc, usually used to show blood (e.g. dying/dead Annette in ROOM6141.RDT).
em_id
- The id of enemy/npc. Game will crash if em slot is empty.red
- Red component, 0 for 100%, 255 for 0%.green
- Green component, 0 for 100%, 255 for 0%.blue
- Blue component, 0 for 100%, 255 for 0%.x_size
- Diameter of shadow in X axis.z_size
- Diameter of shadow in Z axis.x_offset
- Offset of shadow in X axis from centre of enemy.z_offset
- Offset of shadow in Z axis from centre of enemy.
Example:
evt_next();
kage_set(3, ID_EM_0, 16, 191, 191, 1500, 1500, 0, 0);
Add a 1500x1500 red-ish shadow under the centre of Annette (ID_EM_0).
Note: kage_set
must not be called on the first frame, doing this will result in a crash. Ensure you have a evt_next
or sleep
prior to this instruction if calling from room initialization.
0x83: heal()
Heals the player to full health.
Example:
heal();
message_on(0, ID_MSG_0, 0, 255, 255); // Your wounds have been treated.
evt_next();
0x86: poison_ck()
Returns true if the player is poisoned, otherwise false.
Example:
if (poison_ck()) {
message_on(0, ID_MSG_0, 0, 255, 255); // I have treated your poisoned wounds.
evt_next();
poison_clr();
} else {
message_on(0, ID_MSG_1, 0, 255, 255); // If you are ever poisoned, I can help you.
evt_next();
}
0x87: poison_clr()
Clears the poison status if the player is poisoned.
Example:
message_on(0, ID_MSG_3, 0, 255, 255); // Blue herbs are growing here, will you use one?
evt_next();
if (ck(FG_MESSAGE, F_QUESTION, 0)) {
poison_clr();
}
0x88: sce_item_ck_lost(u8 item, u8 count)
Removes an item from the player's inventory and returns true, or false if the player did not have the specified item.
item
- The item to remove, e.g. ITEM_INKRIBBON.count
- The amount to remove, e.g. 1.
If the player has the specified item, but less that the specified count, the function still returns true and the item is fully lost.
Example:
sce_item_ck_lost(ITEM_INKRIBBON, 2);
if (sce_item_ck_lost(ITEM_BLUECARD, 1)) {
message_on(0, ID_MSG_0, 0, 255, 255); // You used the key card
evt_next();
} else {
// Player did not have blue key card
message_on(0, ID_MSG_0, 0, 255, 255); // A key card is required.
evt_next();
}