Skip to content

LunaScript Autocode scripts

迎春心情 edited this page May 8, 2024 · 8 revisions

Content

About LunaScript

LunaScript - is the built-in scripting system of TheXTech engine. It's a minor and backwards-compatible dialect of the LunaDLL Autocode, the assembly-like script language used in the LunaDLL extension hack for the Super Mario Bros. X 1.3 game made in 2013 and maintained until 2015 when it got turned into LunaLua after adding the Lua script language support.

This scripting language is only supported in order to bring old levels and episodes originally made with LunaDLL Autocode to work on the TheXTech engine even on a different processor architecture. The LunaScript was introduced in TheXTech version 1.3.6.

We don't recommend you use this scripting language for your creations (but you still can use it). There are plans to introduce the Lua-based scripting API which is easier to maintain and has more scalability and functionality power. See the issue for more details.

Difference between LunaScript compared to LunaDLL Autocode

  • The sub-system is called "LunaScript version 9" at TheXTech.
  • Doesn't require the installation: the sub-system is the native embedded part of the engine itself.
  • Can work on non-Windows operating systems and on non-x86 processors.
  • It doesn't provide direct memory access because of binary incompatibility. Instead, TheXTech has the abstract layer (called "memory emulator") that maps direct memory addresses at scripts into actual native fields of TheXTech with the proper raw type conversion. It won't cover all of SMBX's internals, except for well-known addresses used in released episodes.
  • Allows comments placed at the end of code lines.
  • Introduces some new commands such as OnEvent, CancelSMBXEvent, SFXPreLoad, etc.
  • The SFX command is able to play more sound formats such as OGG, FLAC, MP3, VOC, etc.
  • The LoadImage command is able to load PNG and GIF with native transparency and alpha-channel, and JPEG and BMP with a selected colour as a transparent.
  • Variables bank is no longer saving in the episode directory through LunaSavedVarsX.txt files. Instead, the variables bank is now written into SAVX gamesave file directly.
  • Death counter no longer works globally. Now, the death counter works for every episode per game save.
  • Has native UTF-8 support for filenames and string data.

Tutorial

Based on the original tutorial for LunaDLL made by Kil

Using lunadll.txt scripts

The most important things to think about are the action you want to do and the point at which the action should happen. The scripting engine only deals with those two things: A time (condition), and an action.

Example times:

  • While loading a level
  • Always
  • In section 1
  • In section 2
  • When a timer runs down
  • When touching a block of a certain type
  • When typing a custom cheat code
  • When a condition is met such as NPC with ID 138 no longer exists

Example actions:

  • Change the player's character
  • Change the powerup
  • Change the reserve powerup
  • Play a sound
  • Change the music
  • Show text
  • Trigger an event
  • Manipulate the game's global variables through the memory emulator somehow

The basics

The first step is creating a file named lunadll.txt in your level folder. That's it.

Structure

There are only 4 things you'll be writing in your lunadll.txt.

1) Time designator - #

The # designates a time of action. They mostly correspond to sections of the level

  • #-1 means during level load
  • #0 means "always"
  • #1 means section 1 (section 0 for 0-based numeration)
  • #2 means section 2 (section 1 for 0-based numeration)
  • ...
  • #21 means section 21 (section 20 for 0-based numeration)
  • #1000 and up is an advanced feature for designating custom events/custom blocks of commands

Once you write say #-1, then all commands you write from then on will be run during the level load time of your level, until you write #1 or some other designator.

2) Comments - // comment

Any line that contains a // will be understood as a comment. Comments are only for humans to read, and the scripting engine ignores them. They're actually important for remembering just what the hell your code is supposed to do, and for others to understand what your code does.

Note: at LunaScript dialect, comments also can be written after the command line:

#0 // comment after time designator
// comment line
Kill,0,0,0,0,0,0   // comment after command

Original LunaDLL Autocode doesn't support this.

3) Commands - Kill,0,0,0,0,0,0

Commands are the most important part of LunaScript scripts, and the most complicated. There's no way to remember them all or what all of the parts of one do usually, so you have to check the reference.

Structure of the typical command CommandName,1,2,3,4,5,6 or command with a reference using $Reference,CommandName,1,2,3,4,5,6

They mostly have the same parts, separated by commas:

CommandName - The name of the command "CommandName" - This first part is the command name. It's the easiest part to remember and explains what the command does, usually. This one kills something. "Infinite flying" activates infinite flying for the player, and so on.

After each command name, there are 6 parameters separated by commas. What each one does is specific to each command, and they're quite hard to remember, which is why you'll have to refer to the reference. But there is some underlying pattern.

1 - The target parameter The first number is usually the "target" of the command. If you want the command to target the player, 0 is usually the player. If you want to target the "key" NPC (the thing you pick up that opens locked doors), well you need to target the key's NPC ID (the key is NPC ID 38).

2, 3, 4 - Options Parameters 2, 3, and 4 are extra parameters that can mean just about anything, but for a lot of commands that aren't complicated, they usually aren't used and are just 0. Check the command reference.

5 - Active time Parameter 5 is virtually always the "active time" specifier. Basically, it's how long the command will "run" before finally deleting itself. 0 means it never runs out. 1 means it lasts 1 frame. 60 means it lasts 60 frames (1 second), and so on.

Keep in mind that commands don't run at all unless they're in the section you're in. A command in #21 with 1 frame active time will sit there forever until the player actually gets to section 21, and then it will run once and then die.

6 - Text Unlike the others, the 6th parameter can also be entered as text but is oftentimes just left as 0 anyway. It's usually where you type messages, decimal numbers, and options.

$Reference - the variable name reference An additional string is written at begin of the command with a dollar $ sign prefix. It's used in cases when a command needs two strings to proceed. For example, the LoadPlayerVar command needs a data type field as a "Text" and the target variable name as a "reference"; or the ShowVar that uses the "reference" for the variable name, and the "text" for the label to print at the left side of the variable.

That's all you need to know about commands.

4) Script footer - #END

The #END is a special string you should put at the end of your .txt file. It's pretty simple - just put #END after everything else.

Examples

Here are a bunch of examples in ascending levels of complexity with lots of comments:

Basic filter script

#-1
FilterToBig,0,0,0,0,0,0
#END

That's the whole thing. First, we have the level load designator, which is where you normally want a filter. FilterToBig lowers Demo's powerup down to mushroom level if she has anything higher than a mushroom, and does nothing if she's small.

FilterToBig's 6 parameters do nothing except the 5th, the active time. It's set to 0 which means "always", but since it's in the level load section, it only works for that 1 frame when the level is loading. If this were in section #0, Demo would never be able to get a power-up higher than a mushroom because she would constantly be filtered to bigness.

Basic filter script 2

#-1
FilterMount,0,0,0,0,0,0
#END

The same thing as filter 1, except it, deletes your mount/Yoshi/shoe if you have one.

Basic filter script 3

// Section 2
#2
FilterReservePowerup,0,0,0,0,180,0
#END

Let's change things up. The designator is now section 2, and the active time is 180. That means when you enter section 2, the player will constantly have his reserve powerup removed over the span of 3 seconds. Why you would actually do this is another story.

Bad Example

#-1
InfiniteFlying,0,0,0,0,0,0
#END

This is a bad example because the command exists in the level load section. This command in particular doesn't work unless it's continuously active, and in the level load section, it won't actually do anything. It should be under #0

Multi-character filter

#-1

// Filter Raocow to Demo
FilterPlayer,0,4,1,0,0,0

// Filter Princess to Demo
FilterPlayer,0,3,1,0,0,0

#END

This example removes the possibility of Raocow or Princess being in your level. FilterPlayer only has 3 relevent parameters. #2 is the character you want to filter out, and #3 is the character you want it to filter to. 1 is Demo, 2 is Iris, etc. #5 is the active time as usual, but since it's the load level section, any amount is fine.

Fairness

Supported by LunaDLL version 7+

#0
ClearInputString,0,0,0,0,0,0
#END

What might that do? #0 is the always section, and ClearInputString... clears the keyboard input buffer. With an active time of 0, it never stops. The keyboard input buffer is filled with keyboard strokes and used to identify when a cheat code is entered. Clearing it constantly means no one will be able to enter a cheat code.

Full level

#-1

// Filter Demo to Sheath
FilterPlayer,0,1,5,0,0,0

#0
// Print the word "HI" at x:300 y:300 on the screen, with font 3
ShowText,0,300,300,3,0,Hi

#1

ShowText,0,200,400,3,0,ISN'T IT HARD TO SEE WITH ALL THIS TEXT IN THE WAY?

#2
// Filter a bunch of stuff and play sound effect ID 10 for no reason
SFX,10,0,0,0,0,0
FilterToSmall,0,0,0,0,1,0
FilterReservePowerup,0,0,0,0,1,0
FilterMount,0,0,0,0,1,0

#END

This is mainly here to illustrate the syntax of having a bunch of different things in one script (basically there aren't many rules in the way of syntax)

Modify boss life

#1
// Set the hit counter of NPCs of type 209 (mother brains) in section 1 to 9 hits
AT_SetHits,209,1,9,0,1,0
#END

NPCs in this game don't have health. They have a hit counter. So a mother brain with 9 hits has 1 hit left. A Birdo with 1 hit has 2 hits left. A mother brain with -10 hits has 20 hits left. Only NPCs that can normally get hit more than once can have their hit counts manipulated. To find the NPC ID of an NPC, check the graphics folder or a list of NPC IDs. Currently, there's no way to specify one single NPC among many that may be at your level. The command runs for every NPC of the type you specify. This goes for all NPC and blocks commands actually.

Trigger an event

Supported by LunaDLL version 7+

#1
TriggerSMBXEvent,0,0,0,0,1,MyEvent
#END

This will start the event named "MyEvent" (assuming you put such an event in your level). Pretty simple.

Is this working?

#0
// Show script info on the screen
DebugPrint,0,0,0,0,0,0
#END

DebugPrint is a command that prints some basic info about how the scripts are running. It's useful for figuring out whether lunadll is even working at all. If you see it reporting that thousands of events are being spawned for no good reason, you should probably recheck your script.

Double the player's lives

#1

// Multiply the decimal amount at the variable mapped to location 0x00B2C5AC by memory emulator by 2
MemAssign,0x00B2C5AC,2,3,0,1,f

#END

This is a memory emulator command, and it's the most complicated thing you're going to find here. You can skip this part and just not use memory emulator manipulation commands and do just fine.

Original LunaDLL had direct memory manipulation support, primarily used by hackers to research/manipulate the binary data of the original game. In order to preserve old levels and episodes made using such manipulations, TheXTech provides an emulator of SMBX memory to let old scripts think they work with the SMBX memory.

The MemAssign performs an operation on a selected variable mapped to an emulated memory location in a global variable section. The game remembers all sorts of things in this space. Lives, coins, the state of switches, timers for everything, score, pointers to level names, directory names, state info like whether or not you're in the editor, in battle mode, how many players there are, how many NPCs there are...

Unfortunately, there are not all known global variables had been mapped by the memory emulator: the memory emulator knows only variables that are known to be used in scripts at the rest of the released LunaDLL levels and episodes. An example address 0x00B2C5AC just happens to be where the lives counter is always located.

If you know the address of something and the format, you can do basic operations on it with MemAssign. The 3rd parameter in 0x00B2C5AC,2,3 is the 3, which is the operation to perform, here is multiplied. The 2nd is 2, meaning multiply by 2.

Operation types

  • 0 = Assign
  • 1 = Add
  • 2 = Subtract
  • 3 = Multiply <<
  • 4 = Divide
  • 5 = XOR

Examples:

  • MemAssign,0x00B2C5AC,2,3 means multiply the value at 0x00B2C5AC by 2.
  • MemAssign,0x00B2C5AC,2,2 would be subtracted lives by 2.
  • MemAssign,0x00B2C5AC,2,1 would add 2 extra lives.
  • MemAssign,0x00B2C5AC,2,0 would set the player's lives to 2.
  • MemAssign,0x00B2C5AC,2,3,0 - The 4th param is 0 since it's unused
  • MemAssign,0x00B2C5AC,2,3,0,1 - An active time of 1. More would keep multiplying the value by 2 and it would quickly max at 99.
  • MemAssign,0x00B2C5AC,2,3,0,1,f - Unfortunately memory is not packed evenly. You need to know the size of the data you're trying to manipulate. For lives, it just so happens that they're a decimal/floating point value (which is odd considering lives are one thing you'd always expect to be a whole number). So the last param specifies what kind of data is operated upon. f means floating point/decimal operation.

Data types

  • b - 1 byte
  • w - 2 bytes (word)
  • dw - 4 bytes (double word)
  • f - 4 bytes decimal/floating point
  • df - 8 bytes decimal / double precision floating point

Where:

  • 1-byte memory is rarely used in this game.
  • 2-byte words are used for many things.
  • 4-byte double words aren't used that often and when they are it's usually something you don't WANT to manipulate this way. The decimal types are very often used for spatial positions of things on the game map.

Custom Events examples

There are many actions you can do with all these commands, but the "time" part of the equation will be lacking unless there are more ways to trigger them besides entering sections.

Any commands designated under a section header higher than 999 will be marked as a custom event block.

// Custom event block 1000. It plays SFX 10 when activated.
#1000
SFX,10,0,0,0,0,0

// Custom event block 1001. It plays SFX 11 and kills you when activated.
// It also calls the delete command on this whole block, so it can't be
// activated any more. (lunadll version 7+)
#1001
SFX,11,0,0,0,0,0
Kill,0,0,0,0,1,0
DeleteEventsFrom,1001,0,0,0,1,0

These are two examples of custom event blocks. They need something to trigger them though. There are many commands that trigger custom events on a condition.

  • Trigger - All it does is trigger a custom event.
  • Timer - It can trigger a custom event when it counts down to 0.
  • BlockTrigger - It can trigger an event when the target touches a certain block.
  • OnInput - Trigger an event when you press a certain something (lunadll 7+)
  • OnCustomCheat - Trigger an event when you type something (lunadll 7+)
  • OnEvent - Trigger an event when SMBX event of given name got triggered (LunaScript 9+)
  • IfCompatMode - Triggers an event according to the currently using compatibility mode of TheXTech: 0 is the modern mode, 1 is the compatibility with SMBX2 fixes, and 2 is the full SMBX 1.3 compatibility mode. (LunaScript 9+)
  • IfSpeedRunMode - Triggers an event according to the currently using speed-run mode of TheXTech: 0 is disabled speed-run, 1 is a speedrun with modern mode, 2 is a speedrun with X2 compatibility mode, and 3 is a speedrun with SMBX 1.3 compatibility mode. (LunaScript 9+)

Cheat a layer

///--Cheat a layer--///
#0
// Wait for you to type "go", then activates custom event 1000
OnCustomCheat,0,0,0,1000,0,go

#1000
// Set layer3 X speed to 2.5 (assuming you added blocks to layer number 3)
LayerXSpeed,3,0,0,0,0,2.5

#END

Really it's self-explanatory. Just type "go" and the layer command is run. By the way, layer 0 is the "default" layer, so 3 is the first custom layer you can make.

There's actually a terrible problem here. The layer command is set to have infinite time. If you type "go" about 15,000 times, your game will start to lag because there are 15,000 commands being run per frame. When you trigger a custom event, it actually means the event is copied into the "always section", so events are like blueprints. The solution is to lower the active time. 1 is fine since the layer doesn't stop.

Player switch

///--Player switch--/// (lunadll 7+)
#0

// When tapping up, copy event 1000
OnInput,0,1,1,1000,0,0

#1000
// If tapping up again in the next 10 frames, copy event 1001
OnInput,0,1,1,1001,10,0

#1001
// Event 1001, switch to the next player and play sound
CyclePlayerRight,0,0,0,0,1,0
SFX,0,13,0,0,0,0

#END

Now it's getting complicated.

The OnInput triggers when you press a certain button. Param2 is 1, which is "up". Param3 is also 1, which means it only detects the key press, not holding them down. With this behaviour, you can simulate the detection of double taps (or more).

In this code, When you push up the first time, another OnInput (event 1000) is activated for 10 frames. This also waits for an up key press. If you successfully double tap, finally, event 1001 is triggered, which switches the character.

Suicide dance

///--Suicide dance--/// (lunadll 7+)
#0

// When tapping up, copy event 1000
OnInput,0,1,1,1000,0,0

#1000
// If tapping down in the next 20 frames, copy event 1001
OnInput,0,2,1,1001,20,0

#1001
// If tapping left in the next 20 frames, copy event 1002
OnInput,0,3,1,1002,20,0

#1002
// If tapping right in the next 20 frames, copy event 1003
OnInput,0,4,1,1003,20,0

#1003
// Tap jump...
OnInput,0,6,1,1004,20,0

#1004
// And then the dance is complete
Kill,0,0,0,0,1,0
DeleteEventsFrom,1003,0,0,0,1,0

#END

A big button sequence. Just a more contrived version of the last one.

Timer

///--Timer--///
#0

// Activate #1000 after 600 frames
Timer,0,1000,1,0,600,0

// Time's up
#1000
Kill,0,0,0,0,1,0

#END

This is mainly about Timer. The timer is a useful command for automatic repetition or... timing. It activates the custom event specified in its 2nd param whenever it reaches 0. Param 3 specifies whether or not to display the timer at the top right of the screen. Param 4 specifies whether or not the timer should reset itself whenever it reaches 0. 5 is both the frame countdown time and the time it should reset itself to if it's set to reset.

Pushable layer

///--Pushable layer--///
#0

//Apply permanent friction/air resistance to layer 3
DeccelerateLayerX,3,0,0,0,0,0.02

//Trigger event 1000 when a wooden square pushed from right
BlockTrigger,0,1,2,1000,0,0

//Trigger event 1001 when wooden square pushed from left
BlockTrigger,0,1,4,1001,0,0


//Move layer left, stop when length runs out
#1000
AccelerateLayerX,3,-2,0,0,2,-0.1

//Move layer right, stop when length runs out
#1001
AccelerateLayerX,3,2,0,0,2,0.1

#END

If you put some wood blocks (Block ID 1) on layer 3, this script will create the illusion that you're pushing them around by running into them from the sides.

DeccelerateLayerX when active slows a layer (towards 0) by the amount in param 6. This simulates something like friction/air resistance.

BlockTrigger triggers custom events when the target interacts with a certain block type from a certain direction.

BlockTrigger,0,1,2,1000,0,0

  • 0 = player triggers the event, 1+ would be NPC IDS
  • 1 = the Block ID
  • 2 = The direction the block should be touched from to trigger. 1=U 2=R 3=D 4=L
  • 1000 = The event to trigger
  • 0 = active time (forever)
  • 0 = unused

Accelerate layer is just the opposite of decelerating. The second param is the max speed it'll be allowed to accelerate to.

Play a custom sound effect when you collect the axe

///--Play custom sound effect when you collect the axe--/// (luna version 7+)
#0

// Activate #1000 after getting the axe (NPC ID 178)
IfNPC,178,2,0,1000,0,once

// Plays a custom sound effect in the level folder "kefka.wav"
#1000
SFX,0,0,0,0,1,kefka.wav

#END

The IfNPC triggers an event based on an NPC condition. 178 means the condition is for axe NPCs. 2 means "no longer exists", so it triggers once the axe is gone.

Infinite looping gimmick

///--Infinite looping gimmick--///
#0

// Just starts the looping events with #1000
Trigger,0,1000,0,0,0,0

// All enemies friendly, and trigger 1001
#1000
Timer,0,1001,1,0,200,0
NPCMemSet,-1,0x46,0xFFFF,0,200,short
ShowText,0,400,550,3,200,YAY!

// All enemies unfriendly, and trigger 1000
#1001
Timer,0,1000,1,0,200,0
NPCMemSet,-1,0x46,0x0000,0,200,short
ShowText,0,400,550,3,200,GRR!

#END

Starts the friendly enemy event, which calls the unfriendly enemy event when it ends, which calls the friendly enemy event again when that ends. Loops forever. Check the reference to be sure about NPCMemSet and IfNPC.

Judgement

///--Judgement--/// (luna version 7+)
#0

// Constantly check if 0x00B2C8C4 equals 0xFFFF, and if so copy #1000
OnGlobalMem,0x00B2C8C4,0xFFFF,0,1000,0,w

// Constantly check if 0x00B2C8C4 equals 0, and if so copy #1001
OnGlobalMem,0x00B2C8C4,0,0,1001,0,w

#1000
ShowText,0,100,250,3,1,THOU HATH CHEATED

#1001
ShowText,0,100,250,3,1,THOU HATH NOT CHEATED

#END

The OnGlobalMem is a powerful trigger command that activates events based on whatever memory location you like. It just so happens that 0x00B2C8C4 is an address which contains a two-byte value that represents whether or not you've entered a cheat code before. This is the memory that's checked to see if you can save, and it's cleared by the cheat which lets you save again.

OnGlobalMem,0x00B2C8C4,0xFFFF,0,1000,0,w

  • 0x00B2C8C4 = address
  • 0xFFFF = value to check
  • 0 = "equals" operation. 1 check "greater than", 2 checks "less than"
  • 1000 = activate #1000 if the check is true
  • 0 = last forever
  • w = check it like a 2-byte value

Remember that either trigger is constantly being activated every frame because the test is either true or untrue, so one ShowText will be copied per frame. The ShowText commands must not have an active time of 0, because then the old ones would never expire while new ones are constantly being added every frame. You'd end up with thousands of identical showtext commands being spawned and you would lag out really quickly.

Hide layer when a player is Character 4 or Character 5

///--Hide layer when player is Raocow or Sheath--/// (luna version 7+)
#0

// Constantly check if player memory at 0xF0 is greater than 3, and if so copy #1000
OnPlayerMem,0xF0,3,1,1000,0,w

// Trigger an smbx event, assuming you added one to your level with this name
#1000
TriggerSMBXEvent,0,0,0,0,1,layerhide

#END

It's almost the same as OnGlobalMem, but the player doesn't have a fixed memory address, so you use a short offset this time. It just so happens that 0xF0 is the offset to the memory which contains what character you currently are. To find more offsets, check the reference at the end of the document.

First, keep in mind that these are the character IDs

  • 0 = invalid
  • 1 = demo
  • 2 = iris
  • 3 = princess
  • 4 = raocow
  • 5 = sheath

OnPlayerMem,0xF0,3,1,1000,0,w

  • 0xF0 = target is player memory offset 0xF0, which contains the character ID
  • 3 = value to test against is 3
  • 1 = operation to perform is "greater than"
  • 1000 = 1000 is the event to trigger if the character ID is greater than 3
  • 0 = active forever
  • w = check the memory like it's a 2-byte word

The same exact thing

///--The same exact thing--/// (luna version 7+)
#0

InfIniteFlying,0,0,0,0,0,0

PlayerMemSet,0,+0x170,50,0,0,w

These both do the exact same thing. InfiniteFlying is just an easier to understand 
wrapper around the same operation.


///--Random refill--/// (luna version 7+)
#-1
// Set coins to 0 on level load
MemAssign,0x00B2C5A8,0,0,0,1,w

#0
// Whenever you have more than 0 coins, activate #1000
OnGlobalMem,0x00B2C5A8,0,1,1000,0,w

// Continuously reset coins to 0
MemAssign,0x00B2C5A8,0,0,0,0,w

#1000
// Random trigger called when you get a coin. 
// One of four choices is chosen with an even* 25% chance.
// #1003 has a 50% chance since it's listed twice.
TriggerRandom,1001,1002,1003,1003,1,0

#1001
// Become small
FilterToSmall,0,0,0,0,1,0
ShowText,0,200,200,3,60,TRIGGERING 1001

#1002
// Become big
PlayerMemSet,0,0x112,2,0,1,w
ShowText,0,200,300,3,60,TRIGGERING 1002

#1003
// Become firey
PlayerMemSet,0,0x112,3,0,1,w
ShowText,0,200,400,3,60,TRIGGERING 1003

#END

The comments explain it all.

Scroll text up

///--Scroll text up--/// (luna version 7+)
#0

// The command we'll modify
ShowText,0,300,700,3,0,FAREWELL...

// This command won't be modified (and won't appear because it's off-screen)
ShowText,0,300,700,3,0,FAREWELL?

// Modify 3rd param, by 1, subtraction, section #0, forever, "FAREWELL..."  what?
ModParam,3,1,2,0,0,FAREWELL...

#END

ModParam is a powerful command that can also totally foul everything up beyond imagining. It changes the parameters of other commands. The challenge is identifying the commands. Param 4 and 6 are used to specify which command you're looking for. Param 4 is which # section the command is in, and Param 6 must match the text exactly of the command you want to modify. Since most commands don't use the text you can just write anything in them and then match it later with ModParam.

ModParam,3,1,2,0,0,FAREWELL...

  • 3 = modify the third param (ShowText's Y coordinate)
  • 1 = amount to modify by
  • 2 = modify by subtracting
  • 0 = look in #0 for the command to modify
  • 0 = modify forever
  • FAREWELL... = the command to modify will have this in the text field

Reference

In the original LunaDLL tutorial, there was a table of memory address references. If you need the memory mapping table, please see this page.

Clone this wiki locally