Added a section for Map Scripts and made some references to PoryScript

tustin2121 2023-04-10 18:39:02 -04:00
parent 56dc9951e6
commit f3879b0e3d
1 changed files with 220 additions and 47 deletions

@ -6,29 +6,36 @@ If you already know XSE scripting from binary hacking, you should be able to pic
To start, you don't need any special scripting tools such as XSE - all you need is a decent text editor (such as Notepad++ or VSCode) and [PoryMap](https://github.com/huderlem/porymap/releases) to get started! It's easy to modify existing scripts - you simply edit the “scripts.inc” file as needed and hit "Save". To start, you don't need any special scripting tools such as XSE - all you need is a decent text editor (such as Notepad++ or VSCode) and [PoryMap](https://github.com/huderlem/porymap/releases) to get started! It's easy to modify existing scripts - you simply edit the “scripts.inc” file as needed and hit "Save".
Table of Contents: If you're using [PoryScript](https://github.com/huderlem/poryscript), all of the below will still apply, but you will be working with a slightly different syntax. We'll touch on this in the first section, but for the full syntax changes, see PoryScript's Readme.
- [Labelling](#Labelling)
- [Simple Text Scripts](#Simple-Text-Scripts)
- [Flags](#Flags)
- [Variables](#Variables)
- [Buffers](#Buffers)
- [Player Gender Check](#Player-Gender-Check)
- [Hide/Show Map Objects](#HideShow-Map-Objects)
- [Give, Take, & Check for Items](#give-take--check-for-items)
- [Giving Pokémon](#giving-pok%C3%A9mon)
- [Movement](#Movement)
- [Random](#Random)
- [Pokémarts](#pok%C3%A9marts)
- [Weather](#Weather)
- [Door Commands](#Door-Commands)
- [Display Pokémon Sprite](#display-pok%C3%A9mon-sprite)
- [Screen Fading Effects](#Screen-Fading-Effects)
- [Play Pokémon Cry](#play-pok%C3%A9mon-cry)
- [Time-Based Scripts](#time-based-scripts)
- [Sound Effects & Fanfares](#sound-effects--fanfares)
- [Advanced Messages](#Advanced-Messages)
# Labelling Table of Contents:
- [Foundation](#Foundation)
- [Labelling](#Labelling)
- [Simple Text Scripts](#Simple-Text-Scripts)
- [Flags](#Flags)
- [Variables](#Variables)
- [Map Scripts](#Map-Scripts)
- [Basic Commands](#Basic-Commands)
- [Buffers](#Buffers)
- [Player Gender Check](#Player-Gender-Check)
- [Hide/Show Map Objects](#HideShow-Map-Objects)
- [Give, Take, & Check for Items](#give-take--check-for-items)
- [Giving Pokémon](#giving-pok%C3%A9mon)
- [Movement](#Movement)
- [Random](#Random)
- [Pokémarts](#pok%C3%A9marts)
- [Weather](#Weather)
- [Door Commands](#Door-Commands)
- [Display Pokémon Sprite](#display-pok%C3%A9mon-sprite)
- [Screen Fading Effects](#Screen-Fading-Effects)
- [Play Pokémon Cry](#play-pok%C3%A9mon-cry)
- [Time-Based Scripts](#time-based-scripts)
- [Sound Effects & Fanfares](#sound-effects--fanfares)
- [Advanced Messages](#Advanced-Messages)
# Foundation
## Labelling
If you look in “data\maps”, you'll find folders pertaining to each in-game map. Each of these map folders has their own “scripts.inc” file, where the scripts for each map are contained. You can also open a map's scripts by clicking "Open Map Scripts" in the top right corner of PoryMap's Events tab. If you look in “data\maps”, you'll find folders pertaining to each in-game map. Each of these map folders has their own “scripts.inc” file, where the scripts for each map are contained. You can also open a map's scripts by clicking "Open Map Scripts" in the top right corner of PoryMap's Events tab.
@ -38,8 +45,58 @@ You can technically name your labels whatever you like so long as they are uniqu
To assign a script to a map object, you'll want to highlight it and put the script's label in the "Script" box outlined in red above. To assign a script to a map object, you'll want to highlight it and put the script's label in the "Script" box outlined in red above.
In the project, you'll usually be including scripts in `script.inc` files inside each map folder. You'll also find other `*.inc` files and `*.s` files with script in them as well. In pokeemerald, scripts are implemented as assembler macros, and will all turn into a lot of byte code upon compiling. You don't need to know the details to work with scripts, though. Here's an example of what a sample `script.inc` file looks like:
# Simple Text Scripts ```gas
DewfordTown_House2_MapScripts::
.byte 0
DewfordTown_House2_EventScript_Boy::
lock
faceplayer
msgbox DewfordTown_House2_Text_BrawlySoCool, MSGBOX_DEFAULT
release
end
DewfordTown_House2_Text_BrawlySoCool:
.string "Wow, you bothered to cross the sea\n"
.string "to visit DEWFORD?\p"
.string "Did you maybe come here because you\n"
.string "heard about BRAWLY?\p"
.string "He's so cool…\n"
.string "Everyone idolizes him.$"
```
Note how the labels are followed by one or two colons. One colon indicates that the label is private. Two indicates that the label is global. If you want to reference your label outside this file (like to paste it into PoryMap like above), it needs to be global.
### PoryScript
If you're using PoryScript, the files you'll be editing will be called `script.pory` files, or any file with the `.pory` extension. PoryScript will take these `.pory` files and turn them into `.inc` files for you. PoryScript's syntax is different, so that it can better understand your script code. Here's the same script file as above in PoryScript format:
```pory
mapscripts DewfordTown_House2_MapScripts {}
script DewfordTown_House2_EventScript_Boy {
lock
faceplayer
msgbox (DewfordTown_House2_Text_BrawlySoCool, MSGBOX_DEFAULT)
release
}
text DewfordTown_House2_Text_BrawlySoCool {
"Wow, you bothered to cross the sea\n"
"to visit DEWFORD?\p"
"Did you maybe come here because you\n"
"heard about BRAWLY?\p"
"He's so cool…\n"
"Everyone idolizes him.$"
}
```
Note that the main difference is that any parameters for commands must be surrounded in parenthesis. PoryScript also does a lot of if statement stuff for you, but we'll cover that later.
## Simple Text Scripts
Here is an example of a simple text script: Here is an example of a simple text script:
@ -144,27 +201,27 @@ A few more notes about `MSGBOX_YESNO`:
- The `compare VAR_RESULT` line checks the result of the player's answer - 0 for "No", 1 for "Yes" (or you can use the constants `NO` and `YES`, or `FALSE` and `TRUE` respectively). More about vars later! - The `compare VAR_RESULT` line checks the result of the player's answer - 0 for "No", 1 for "Yes" (or you can use the constants `NO` and `YES`, or `FALSE` and `TRUE` respectively). More about vars later!
- The `goto_if_eq` line means the script will go to a new label if the result is equal to what we specified above, in this case, 0. If it's *not* equal to that, the script will continue as normal. - The `goto_if_eq` line means the script will go to a new label if the result is equal to what we specified above, in this case, 0. If it's *not* equal to that, the script will continue as normal.
In the remaining sections, we're going to go over several common commands and what you can do with them. If you want a list of all commands, you can look in `asm/macros/event.inc`, which defines all the commands that can be used in the scripts. All of the commands have an explanation of what they do above them. ## Flags
# Flags
Think of a flag as an on or off switch - it can be either set ("ON") or cleared ("OFF"). They can be used to track progress, whether the player has completed a certain event or to control visibility of objects on a map. Think of a flag as an on or off switch - it can be either set ("ON") or cleared ("OFF"). They can be used to track progress, whether the player has completed a certain event or to control visibility of objects on a map.
Setting and clearing flags is nice and simple; all we have to do is: Setting and clearing flags is nice and simple; to set a flag we use:
```gas ```gas
setflag [Flag] setflag [Flag]
``` ```
To set a flag, and to clear one: To clear a flag we use:
```gas ```gas
clearflag [Flag] clearflag [Flag]
``` ```
To check if a flag is set, we'd do: To check if a flag is set, we do:
```gas ```gas
goto_if_set [Flag], [Script Label] goto_if_set [Flag], [Script Label]
``` ```
(PoryScript can use a common `if` function, such as `if (var(FLAG_NAME))`. PoryScript will convert such if statements and blocks into a `goto_if_set` command for you.)
If you open `include\constants\flags.h`, you'll find a list of all the in-game flags. Some flags have special uses, e.g. for the activation of the Running Shoes or certain menus. In this demo script, we'll set the flag that activates the Pokédex menu: If you open `include\constants\flags.h`, you'll find a list of all the in-game flags. Some flags have special uses, e.g. for the activation of the Running Shoes or certain menus. In this demo script, we'll set the flag that activates the Pokédex menu:
```gas ```gas
Script_CheckDexFlag:: Script_CheckDexFlag::
@ -191,7 +248,7 @@ Text_DexFlagSet:
If the flag is set, the script jumps ahead to the `Script_DexFlagSet` label and the string "Pokédex flag is set" will display. If the flag is cleared, the script continues, displays "Pokédex flag is cleared" and then sets the flag before the end. If the flag is set, the script jumps ahead to the `Script_DexFlagSet` label and the string "Pokédex flag is set" will display. If the flag is cleared, the script continues, displays "Pokédex flag is cleared" and then sets the flag before the end.
# Variables ## Variables
The list of variables can be found in `include\constants\vars.h`. The list of variables can be found in `include\constants\vars.h`.
```gas ```gas
@ -221,8 +278,124 @@ A list of the different conditions for `goto_if`/`call_if`:
- `goto_if_le` = Less Than or Equal To - `goto_if_le` = Less Than or Equal To
- `goto_if_ge` = Greater Than or Equal To - `goto_if_ge` = Greater Than or Equal To
So far we've covered how to give an object a script that runs when you interact with it. If you're itching to go on that, you can skip to the [Basic Commands](#Basic-Commands) section below and start playing around with scripting. When you want to do more advanced things, like make a cutscene start upon entering a map, continue onward.
# Buffers ## Map Scripts
Map scripts are scripts run when something happens in a given map. Every map has a map scripts header. Here's an example from the game:
```gas
MossdeepCity_StevensHouse_MapScripts::
map_script MAP_SCRIPT_ON_LOAD, MossdeepCity_StevensHouse_OnLoad
map_script MAP_SCRIPT_ON_TRANSITION, MossdeepCity_StevensHouse_OnTransition
map_script MAP_SCRIPT_ON_FRAME_TABLE, MossdeepCity_StevensHouse_OnFrame
.byte 0
MossdeepCity_StevensHouse_OnLoad:
call_if_unset FLAG_SYS_GAME_CLEAR, MossdeepCity_StevensHouse_EventScript_HideStevensNote
end
MossdeepCity_StevensHouse_EventScript_HideStevensNote::
setmetatile 6, 4, METATILE_GenericBuilding_TableEdge, TRUE
return
MossdeepCity_StevensHouse_OnTransition:
call_if_eq VAR_STEVENS_HOUSE_STATE, 2, MossdeepCity_StevensHouse_EventScript_SetStevenPos
end
MossdeepCity_StevensHouse_EventScript_SetStevenPos::
setobjectxyperm LOCALID_STEVEN, 6, 5
setobjectmovementtype LOCALID_STEVEN, MOVEMENT_TYPE_FACE_UP
return
MossdeepCity_StevensHouse_OnFrame:
map_script_2 VAR_STEVENS_HOUSE_STATE, 1, MossdeepCity_StevensHouse_EventScript_StevenGivesDive
.2byte 0
```
In PoryScript, there's a special kind of block called the `mapscripts` block. If you converted the above to PoryScript, it would look something like this:
```pory
mapscripts MossdeepCity_StevensHouse_MapScripts {
MAP_SCRIPT_ON_LOAD: MossdeepCity_StevensHouse_OnLoad
MAP_SCRIPT_ON_TRANSITION {
if (var(VAR_STEVENS_HOUSE_STATE) == 2) {
setobjectxyperm (LOCALID_STEVEN, 6, 5)
setobjectmovementtype (LOCALID_STEVEN, MOVEMENT_TYPE_FACE_UP)
}
}
MAP_SCRIPT_ON_FRAME_TABLE [
VAR_STEVENS_HOUSE_STATE, 1: MossdeepCity_StevensHouse_EventScript_StevenGivesDive
]
}
script(local) MossdeepCity_StevensHouse_OnLoad {
if (!flag(FLAG_SYS_GAME_CLEAR)) {
setmetatile (6, 4, METATILE_GenericBuilding_TableEdge, TRUE)
}
}
```
The Map Scripts are a series of special scripts that are run at given times during a map's lifecycle. In vanilla, these events are:
- `ON_DIVE_WARP` - Run after the player chooses to dive/emerge when surfing. Not used often.
- `ON_TRANSITION` - Run after a map transition has started. This means after a fade out or when walking between maps that are connected.
- The game uses this event to set map-specific flags/vars, update object template attributes, and set the weather.
- `ON_LOAD` - Run after the game has loaded the map's layout into memory, before it is drawn to the screen.
- Usually used to conditionally set metatiles. If you set metatiles outside of an `ON_LOAD` script, you will need to run the script command `special DrawWholeMapView` to redraw the map and see the metatile changes.
- `ON_RESUME` - Run after the end of a map load, and any time the map "resumes".
- This includes things like exiting a full-screen menu, like the bag, or finishing a battle.
- This also includes when entering the map for the first time after a load.
- This is often used by the game to add or update objects, or to restart "per step callbacks".
- `ON_RETURN_TO_FIELD` - Run after `ON_RESUME`, but only upon returning to the field from a battle or menu, and not when the map first loads.
- `ON_WARP_INTO_MAP_TABLE` - A script table, evaluated after the map's objects are loaded, before the map is drawn and the screen fades in.
- `ON_FRAME_TABLE` - A script table, evaluated every frame while the player is able to walk around a map.
- This is where you start your cutscenes upon entering a new map.
Most of the above events take one script label, a script which is run when the event takes place. The two script tables, however, are a little different. The following is an example of a script table:
```gas
MossdeepCity_StevensHouse_OnFrame:
map_script_2 VAR_TEMP_A, 0, EventScript_RunCutscene1
map_script_2 VAR_TEMP_A, 5, EventScript_RunCutscene2
map_script_2 VAR_TEMP_B, 8, EventScript_RunCutscene3
.2byte 0
```
When the game evaluates a script table, it checks the variable specified to the value specified, and if they match, it runs the script at the end of the same line and stops evaluating the table. If it gets through the whole table without any of the conditions matching, nothing more happens and the game continues normally.
**Note**: The script tables can *only* check variables in the vanilla game. You cannot check a flag this way.
**IMPORTANT**: All map script, with the exception of scripts in the `ON_FRAME_TABLE`, *must* be able to run in a single frame. The map scripts run in a special separate script context for this purpose, and if your script calls on any commands that take multiple frames to evaluate (such as `lock`, `delay`, or any of the `wait*` commands), your game *will* softlock on a black screen.
### Cutscenes on Map Load
You'll find quickly once scripting that if you attempt to put a trigger to run a script down on the same tile the player starts on in a map, the script will not run. That's because triggers only run when the *player* commands their avatar to step on the trigger. If the player ends up on a trigger thanks to warp movement or `applymovement` (see below), the trigger will not trigger.
If you wish to run a cutscene as soon as the map loads, then you'll need to put your script into the `ON_FRAME_TABLE`. As soon as the player would gain control in a map, if the variable matches the value, the game will run the script instead, and you can do a cutscene here.
```gas
MossdeepCity_StevensHouse_MapScripts::
map_script MAP_SCRIPT_ON_FRAME_TABLE, MossdeepCity_StevensHouse_OnFrame
.byte 0
MossdeepCity_StevensHouse_OnFrame:
map_script_2 VAR_TEMP_A, 0, EventScript_RunCutscene1
.2byte 0
EventScript_RunCutscene1:
lock
@ Insert cutscene here
setvar VAR_TEMP_A, 1
release
end
```
Remember in your cutscene script to set the variable you check to run the script to something else before the script ends, or the cutscene will just start over again from the top, forever. Also note that `VAR_TEMP_*` variables are temporary and are reset upon a map change. Which means in the example above, my Cutscene1 would run every time I walk into the map.
# Basic Commands
In the remaining sections, we're going to go over several common commands and what you can do with them. If you want a list of all commands, you can look in `asm/macros/event.inc`, which defines all the commands that can be used in the scripts. All of the commands have an explanation of what they do above them.
## Buffers
Let's start with `bufferleadmonspeciesname`, which displays the species of your player's first party member - just like it says on the tin! Let's start with `bufferleadmonspeciesname`, which displays the species of your player's first party member - just like it says on the tin!
```gas ```gas
@ -280,7 +453,7 @@ buffertrainerclassname [STR_VAR], [TRAINER_ID]
buffertrainername [STR_VAR], [TRAINER_ID] buffertrainername [STR_VAR], [TRAINER_ID]
``` ```
# Player Gender Check ## Player Gender Check
As the title suggests, we'll be looking at how to make conditional messages appear depending on player gender. As the title suggests, we'll be looking at how to make conditional messages appear depending on player gender.
@ -335,7 +508,7 @@ Text_FemaleExample:
``` ```
# Hide/Show Map Objects ## Hide/Show Map Objects
Do you need to make a NPC or other overworld sprite appear or disappear? Let's look at the following commands: Do you need to make a NPC or other overworld sprite appear or disappear? Let's look at the following commands:
```gas ```gas
@ -366,7 +539,7 @@ clearflag [Flag]
addobject [Local ID] addobject [Local ID]
``` ```
# Give, Take, & Check For Items ## Give, Take, & Check For Items
Before you think about the multitude of free items you can give your players, you should always make sure they can actually take the item first! There are two ways of doing this. Before you think about the multitude of free items you can give your players, you should always make sure they can actually take the item first! There are two ways of doing this.
```gas ```gas
@ -460,7 +633,7 @@ Note: There is another command `additem`, which will attempt to add an item to t
Finally: All of these commands take "[Quantity=1]", meaning that you don't have to supply a number of the item to give, and it will default to one if you do not. Finally: All of these commands take "[Quantity=1]", meaning that you don't have to supply a number of the item to give, and it will default to one if you do not.
# Giving Pokémon ## Giving Pokémon
Wondering how to give your player a gift Pokémon or Egg? In order to do this effectively, we'll need to make use of two commands. Wondering how to give your player a gift Pokémon or Egg? In order to do this effectively, we'll need to make use of two commands.
The first is `getpartysize`: The first is `getpartysize`:
@ -562,7 +735,7 @@ Text_PlayerHasFullParty:
This script asks the player whether or not they would like to accept the Egg. After that, if the player responds with "YES", it'll check if they have space in their party before giving it to them. This script asks the player whether or not they would like to accept the Egg. After that, if the player responds with "YES", it'll check if they have space in their party before giving it to them.
# Give & Check For PC Items ## Give & Check For PC Items
Adding an item to the player's PC is just as easy with a single line: Adding an item to the player's PC is just as easy with a single line:
@ -586,7 +759,7 @@ goto_if_ge Script_HasPotionInPC
Should the player have one or more Potions in their PC's item storage, the script will go to the label `Script_HasPotionInPC`. Should the player have one or more Potions in their PC's item storage, the script will go to the label `Script_HasPotionInPC`.
# Movements ## Movements
To make the player / NPCs move in a script, we use the `applymovement` command: To make the player / NPCs move in a script, we use the `applymovement` command:
@ -627,7 +800,7 @@ Note: If an object has a movement script still running on it, a second `applymov
A list of movement types can currently be seen in `asm\macros\movement.inc`. Don't forget to add `step_end` to indicate that your movements are finished! A list of movement types can currently be seen in `asm\macros\movement.inc`. Don't forget to add `step_end` to indicate that your movements are finished!
# Random ## Random
The `random` command can be used to generate a random number within the range you set, and then have the script go to various labels depending on the number. Let's say we wanted a range of 5: The `random` command can be used to generate a random number within the range you set, and then have the script go to various labels depending on the number. Let's say we wanted a range of 5:
@ -682,7 +855,7 @@ Text_RandomOption3:
There are a whole bunch of things you could use this for! I like using it for my generic city/town population NPCs to make them a little bit more interesting, but you can use it for a lot more than that. There are a whole bunch of things you could use this for! I like using it for my generic city/town population NPCs to make them a little bit more interesting, but you can use it for a lot more than that.
# Pokémarts ## Pokémarts
Creating your own shops is straightforward enough with the pokemart command. Creating your own shops is straightforward enough with the pokemart command.
@ -730,7 +903,7 @@ Text_HerbShopEnd:
``` ```
# Weather ## Weather
To manipulate weather in the overworld, use the following commands: To manipulate weather in the overworld, use the following commands:
@ -767,7 +940,7 @@ Note: `setweather` is a separate command from `doweather`, because `doweather` s
The weather list can be found in `include\constants\weather.h`. The weather list can be found in `include\constants\weather.h`.
# Door Commands ## Door Commands
If you need a script that involves having the player or NPCs enter/exit a building, you'll need to know how to manipulate the door animations. If you need a script that involves having the player or NPCs enter/exit a building, you'll need to know how to manipulate the door animations.
@ -794,7 +967,7 @@ setdoorclosed [X], [Y]
These do what you might expect, sets the door at the given coordinates to be open or closed without the animation. These do what you might expect, sets the door at the given coordinates to be open or closed without the animation.
# Display Pokémon Sprite ## Display Pokémon Sprite
You might not use it too often throughout your hack, but showing a Pokémon's sprite can add a little something extra to scripts where the Pokémon is being discussed. You might not use it too often throughout your hack, but showing a Pokémon's sprite can add a little something extra to scripts where the Pokémon is being discussed.
@ -824,7 +997,7 @@ Text_DisplaySpriteDemo:
Coordinates of `10, 3` will display the box containing the specified sprite in the middle of the screen. Coordinates of `10, 3` will display the box containing the specified sprite in the middle of the screen.
# Screen Fading Effects ## Screen Fading Effects
It's easy to give the overworld a "fading out" effect - useful for cutscenes and such - by using the fadescreen command which looks like this: It's easy to give the overworld a "fading out" effect - useful for cutscenes and such - by using the fadescreen command which looks like this:
@ -861,7 +1034,7 @@ Text_FadescreenExample:
``` ```
# Play Pokémon Cry ## Play Pokémon Cry
Using Pokémon cries as a sound effect is another super easy thing to do, all you need is the playmoncry command: Using Pokémon cries as a sound effect is another super easy thing to do, all you need is the playmoncry command:
@ -885,7 +1058,7 @@ Text_CryDemo:
``` ```
# Time-based Scripts ## Time-based Scripts
The `gettime` command can be used to set the values of variables `VAR_0x8000`, `VAR_0x8001`, and `VAR_0x8002` to the in-game hour, minute, and second respectively. To make sure the time is up to date, you should also call `dotimebasedevents` before that. This is an example script for a NPC who gives different greetings depending on the hour: The `gettime` command can be used to set the values of variables `VAR_0x8000`, `VAR_0x8001`, and `VAR_0x8002` to the in-game hour, minute, and second respectively. To make sure the time is up to date, you should also call `dotimebasedevents` before that. This is an example script for a NPC who gives different greetings depending on the hour:
@ -928,7 +1101,7 @@ Text_Night:
You could swap out the values to check for a particular minute or second too, if you wanted to run a very specific event that only occurs at a very specific time! You could swap out the values to check for a particular minute or second too, if you wanted to run a very specific event that only occurs at a very specific time!
# Sound Effects & Fanfares ## Sound Effects & Fanfares
Sound effects can bring a lot of life to overworld gameplay. To play sounds, use the following: Sound effects can bring a lot of life to overworld gameplay. To play sounds, use the following:
@ -958,7 +1131,7 @@ waitfanfare
You can find a list of labels for sound effects and music in `include\constants\songs.h`. You can find a list of labels for sound effects and music in `include\constants\songs.h`.
# Advanced Messages ## Advanced Messages
If you wanna do something more advanced with your message boxes, then it's time to get into some more advanced commands and things. If you wanna do something more advanced with your message boxes, then it's time to get into some more advanced commands and things.