What
For more context, I recommend reading the Quickstart article first. We’re going to take a more in-depth look at sniper_bnuy.ini
, Rusted Moss’s example mod.
sniper_bnuy.ini
[object_list]
controller = enabled
instance = enabled
[controller_events]
room_start = "i = 0; while ( i < instance_number(oenemy_flame_sniper) ) { c = instance_find(oenemy_flame_sniper,i); instance_create_depth(c.x,c.y,30,omod_instance); i += 1; }"
[instance_events]
create = "self.parent = instance_nearest(self.x,self.y,oenemy_flame_sniper);"
draw = "if ( instance_exists(self.parent)) { draw_sprite_ext( spuck_bunny_ears, 0, self.parent.x, self.parent.y-8, 1, 1, 0, c_white, 1 ) }"
step = "if ( instance_exists(self.parent) ) { if ( self.parent.legs == smaya_legs_run ) { self.parent.vsp = self.parent.vsp -4; } }"
This a mod file, and contains the Catspeak code that runs. Let’s break it down.
Object List
[object_list]
controller = enabled
instance = enabled
This section tells Rusted Moss what Game Objects to run code for. In this case, controller
and instance
Game Objects are enabled.
Object Events
[controller_events]
room_start = "i = 0; while ( i < instance_number(oenemy_flame_sniper) ) { c = instance_find(oenemy_flame_sniper,i); instance_create_depth(c.x,c.y,30,omod_instance); i += 1; }"
This is where we define the Catspeak code that runs on each Event. The header ([controller_events]
) tells us what Game Object to run code for, and the key (room_start
) tells us the Event. Inside the "
quotes, as the value for room_start
, is our Catspeak code. It’s not very readable, so I’ll add some line breaks for now and friendly comments.
i = 0
-- For each `oenemy_flame_sniper` in the current room ...
while ( i < instance_number(oenemy_flame_sniper) ) {
-- ... find that Instance ...
c = instance_find(oenemy_flame_sniper, i);
-- ... and make a new `omod_instance` Instance at its position.
instance_create_depth(c.x, c.y, 30, omod_instance);
i += 1;
}
Pretty simple, we’re making an omod_instance
Instance for each oenemy_flame_sniper
in the Room, when we first enter a Room. There’s a few other notes for this code.
- Line Breaks: Because of boring technical reasons, we cannot add line breaks to our Catspeak source code. There are ways around it, but for now we’ll need to remove all of the line breaks. For clarity, I’ll be including line breaks in code examples.
- Gamemaker Functions:
instance_number
,instance_find
, andinstance_create_depth
are all Gamemaker functions. Rusted Moss conveniently exposes every Gamemaker function for us, which is cool. However, because of boring technical reasons, some of these functions just don’t work. oenemy_flame_sniper
: This is one of Rusted Moss’s Game Objects, specifically the enemies found in certain Living Quarters rooms. Similar to Gamemaker functions, Rusted Moss exposes every Game Object as well. Undertale Mod Tool lists every Game Object for our convenience.room_start
: This Event runs when we first enter a Room. A singleomod_controller
Instance is created for us when the game starts, so we don’t need to do anything else for this event to run.omod_instance
: This is a special Game Object, since we get to write code for it! Let’s see what it’s code looks like:
omod_instance
Code
[instance_events]
create = "self.parent = instance_nearest(self.x,self.y,oenemy_flame_sniper);"
draw = "if ( instance_exists(self.parent)) { draw_sprite_ext( spuck_bunny_ears, 0, self.parent.x, self.parent.y-8, 1, 1, 0, c_white, 1 ) }"
step = "if ( instance_exists(self.parent) ) { if ( self.parent.legs == smaya_legs_run ) { self.parent.vsp = self.parent.vsp -4; } }"
Since the game makes an omod_controller
for us, we don’t need to create a controller Instance. However, instance
Game Objects must be created by our code (in this example, the create
event creates our Instances). There are also three events this time, so let’s break it down.
-- Create event, add an Instance variable for the closest `oenemy_flame_sniper`
self.parent = instance_nearest(self.x, self.y, oenemy_flame_sniper);
-- Draw event, draw bunny ears on the head of each "parent" Instance
if ( instance_exists(self.parent)) {
draw_sprite_ext(
spuck_bunny_ears, 0,
self.parent.x, self.parent.y - 8,
1, 1,
0, c_white, 1
)
}
-- Step event, if the parent is "running", make it "jump"
if ( instance_exists(self.parent) ) {
if ( self.parent.legs == smaya_legs_run ) {
self.parent.vsp = self.parent.vsp - 4;
}
}
Each omod_instance
Instance is responsible for managing a single oenemy_flame_sniper
Instance, drawing its ears and jumping. There are a few things to note here.
self.parent
: This is an Instance variable onself
, which is the currently scoped Instance. In most cases, this will be the moddable Instance you’e writing code for, however you can use thewith
statement to change the current scope.legs
andvsp
: These are custom Instance variables onoenemy_flame_sniper
Instances. To find out how what they do, you’ll want to investigate the source code using Undertale Mod Tool.spuck_bunny_ears
: This is a Sprite, all of which are exposed for modding. Because of boring technical reasons, our ability to modify Sprites is slightly limited, but there are hundreds of existing Sprites we can use.create
,draw
, andstep
Events: Unlikecontroller
,instance
Game Object need to be created first (since there is no Instance for the Event to run on).create
runs immediately after creation (halting execution).draw
andstep
both happen each frame, withdraw
triggering afterstep
.