HUD Scripting Tutorial

From OHRRPGCE-Wiki
Jump to navigation Jump to search

Slice Collection Editor - Scripting a HUD:[edit]

This is an extension of the “Slice Collection Editor - Making a HUD” tutorial. If you haven’t followed that, parts of this tutorial may not make sense! If you have not completed that tutorial but still want to follow this one, you may download the slice collection resulted from that tutorial here: TutorialHUD.slice.zip. Make sure to uncompress the file. To import this slice collection into your .rpg file, open the Slice Collection Editor and navigate to an unused collection (or one you aren’t afraid to overwrite). Press F3, then navigate to and select “tutorialHUD.slice” and press enter.

In the previous tutorial, we used the slice collection editor to make a HUD that displays the name and class of 3 heroes. You may have wondered, what’s the point? The HUD we created is completely static, so what use is it in an actual game? Well, let’s answer those questions!

So, we want to change some of the info in the HUD during gameplay. Let’s determine what those things are. On each party member’s box, we want to display

  • The name as the player chose it
  • The hero’s currently equipped item
  • The hero’s current and maximum HP
  • The hero’s walkabout sprite

Tweaking the Slice Collection[edit]

Before we can start scripting, we need to make some changes to the existing slice collection. The biggest change is adding 3 more text slices to each hero’s box for HP display. The text slices will include the current HP value, a “/“ character, and the maximum HP value (eg. 10/10 for a full health hero with 10 maximum HP). Next, we will add lookup codes to every slice we want to alter during gameplay. Lookup codes will help us reference the correct slices in our scripts.

How the Collection Looks Now
01 Hierarchy.png

Here is what the slice tree looks like for each hero’s status box. I added 3 new text slices, each a child of the last to make their positions move relative to one another. These slices will change widths as the HP values change from single to double to triple digit numbers. We want the “/“ to always appear horizontally after the current HP, and we want the max HP to always appear horizontally after the “/“. For this, we use shift+right arrow to set the “/“ text slice as a child of the current HP slice and set the max HP slice as a child of the “/“ slice. Then, we set “Align horiz. to: Right” for the “/“ text slice and the max HP text slice.

Once you’ve created the 3 text slices for HP display, you can use ctrl+c while the current HP text slice is highlighted to copy the slice. Then, navigate to the next hero status box and press ctrl+v to paste the current HP text slice. Its children, the other text slices we created, will also be copied.

Finally, create lookup codes by selecting the “Lookup code: “ parameter of the text slices for the name, equipped item, current HP, and max HP. The names I used were “name,” “item,” “currentHp,” “maxHp,” and “sprite,” but you can name them however makes sense to you. Make sure to set the lookup codes on the appropriate slices so that it resembles the above screenshot.

Pro Tip

Protip.png

You can press f6 in the slice collection editor to move the preview. This is useful if the menu text is preventing you from seeing your changes to the slices. It’s also useful when using non-default resolutions for Custom and Game.

Starting to Script: Changing the Names[edit]

The first step in making our HUD modifiable during gameplay is introducing variables into our script.

plotscript, loadHud, begin
    variable(hud) 
    
    hud := loadSliceCollection(0)
end

This script is very similar to the script from the previous tutorial, with a key difference. First, we create a variable, which is a device that stores values. Instead of just loading the slice collection, we set our variable, called “hud,” to the value returned by “load slice collection(0).” The declaration “hud := “ will shove whatever value comes after the ‘:=‘ into the variable. In this case, “load slice collection” returns a slice handle for the loaded collection.

What is a Slice Handle?
Every slice has a “handle,” which is like a unique reference number to a slice. This number is arbitrary and unpredictable. Think about your school or employee ID number: it’s yours, no one else shares it, it was assigned arbitrarily (meaning, it’s not reliably guessable), and others can use it to look up information about you on file. The slice collection we load has a handle itself, and we store that handle in the variable, “hud.”

This alone doesn’t help us much. The slice collection contains all of the slices we created in the editor, and we need to get handles for all of those to manipulate their data. For example, we’ll be using the script command “set slice text(handle, string id)” (set slice text) a lot. When we change the name “Francine” to something else in our script, we want to use “set slice text,” where the handle argument is the handle for the text slice that currently says “Francine” and the string id argument has our hero’s actual name in it. Eg. “set slice text(name slice, hero’s name)”.

How do we get the handle for the name slice, then? That’s what we created the lookup codes for! Look at the “lookup slice(lookup code, start slice)” script command in the plotscripting dictionary: lookup slice. It “Search[es] for an important slice using a slice lookup code, and return[s] a handle to it…” Perfect! Our name slice has a lookup code called “sli:name.”

02 LookupCode.png

Note another detail about “lookup slice,” however: “You can optionally specify a slice for the lookup slice command to start searching from.” This is important, because we have 3 slices with the same lookup code (one for each hero). Below is an incorrect script

variable(txt)
txt := lookupSlice(sli:name)

The problem with the above script is that it will search all the slices in the entire game and return the first slice it finds with the sli:name lookup code. If we want to get our second hero’s name slice, for instance, we would need to set the txt variable to “lookup slice(sli:name, hero 2’s status box).” So how do we get a reference to hero 1’s status box, hero 2’s, etc.? Let’s take a closer look at our slice collection hierarchy.

03 Hierarchy.png

This image may be a lot to take in all at once, but break it down into pieces. The slice collection itself has a handle returned by “load slice collection(0),” which we’ve stored in the variable, hud. The first child of hud is the container slice highlighted in yellow: “firstChild(hud)” first child. This container is important: it has 3 children, which are the rectangle slices that make up our 3 hero status boxes.

Clarifying Parentage
Parent/child status may be confusing, so I’ll clarify. You may think that the first text slice in the hierarchy (the name text) is the second child of the container slice. However, that text slice is a child of the rectangle, making it a grandchild of the container slice. Therefore, “slice child(container slice, 1)” would not return the text slice, but the second rectangle slice in the hierarchy (the one highlighted yellow).

Knowing that, we can add to our plotscript:

plotscript, loadHud, begin
    variable(hud, txt)
    
    hud := loadSliceCollection(0)
    
    # Hero 0
    txt := lookupSlice(sli:name, sliceChild(firstChild(hud), 0)) # now txt equals the first hero name
    # Hero 1
    txt := lookupSlice(sli:name, sliceChild(firstChild(hud), 1)) # now txt equals the second hero name
    # Hero 2
    txt := lookupSlice(sli:name, sliceChild(firstChild(hud), 2)) # now txt equals the third hero name
end

To actually set the text, we need to get the name of the hero. That can be easily done with the “get hero name(string id, hero)” script command: get hero name. We will use string id 0 for our script, but you can use any string id within the 100-string limit. Once the hero’s name is stored in the string id, we can use “set slice text(name slice, string id)” to make the actual change.

plotscript, loadHud, begin
    variable(hud, txt)
    
    hud := loadSliceCollection(0)
    
    # Hero 0
    txt := lookupSlice(sli:name, sliceChild(firstChild(hud), 0))
    getHeroName(0, 0) # string ID: 0, hero slot: 0
    setSliceText(txt, 0) # text slice handle: txt, string ID: 0
    # Hero 1
    txt := lookupSlice(sli:name, sliceChild(firstChild(hud), 1))
    getHeroName(0, 1) # string ID: 0, hero slot: 1
    setSliceText(txt, 0) # text slice handle: txt, string ID: 0
    # Hero 2
    txt := lookupSlice(sli:name, sliceChild(firstChild(hud), 2))
    getHeroName(0, 2) # string ID: 0, hero slot: 2
    setSliceText(txt, 0) # text slice handle: txt, string ID: 0
end

Now we’ve set all the name slices to the correct hero names. However, the code’s gotten a bit redundant. As a rule of thumb, if you’re ever repeating the same lines of code over and over, there’s likely a way to put the bit of code in a loop! for

For Loops
A for loop has 4 parameters: a variable, a starting value, an ending value, and a step value. The variable should be unused in locations outside the for loop, because it will be overwritten by the for loop. During the for loop:

1. The variable starts at the starting value.

2. The contents of the loop (everything inside the do() block) run.

3. The step value is added to the variable.

4. If the current value of the variable has not surpassed the ending value, return to step 2.


Knowing about for loops, we can rewrite our script like this:

plotscript, loadHud, begin
    variable(hud, txt, hero)
    
    hud := loadSliceCollection(0)
    
    for(hero, 0, 2, 1) do(
        txt := lookupSlice(sli:name, sliceChild(firstChild(hud), hero))
        getHeroName(0, hero) # string ID: 0, hero slot: hero
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
    )
end

Much shorter! Additionally, if we want to add more heroes and more status boxes to the slice collection, now all we need to do is change the ending value of the for loop. If we have 10 heroes instead of 3, the line would read “for(hero, 0, 9, 1) do(“ instead. Sometimes it can feel more confusing to use things like for loops instead of simply repeating the lines, but it’s a good habit. If you had a hundred heroes to account for, for instance, you really wouldn’t want to copy and paste that all those times!

On that note, now is a good time to add a check to make sure the hero exists before we start getting their info. This is generally a good practice, especially when looping through ID numbers like we are here.

The command “hero by slot(where)” returns a hero ID number in the slot you give it, or -1 if there’s no hero in that slot hero by slot. The first line of our for loop should “continue” (skip to the next iteration of the loop) if “hero by slot(hero)” returns -1.

plotscript, loadHud, begin
    variable(hud, txt, hero)
    
    hud := loadSliceCollection(0)
    
    for(hero, 0, 2, 1) do(
        if(heroBySlot(hero) == -1) then(continue) # skip everything after this if no hero is in this slot
        txt := lookupSlice(sli:name, sliceChild(firstChild(hud), hero))
        getHeroName(0, hero) # string ID: 0, hero slot: hero
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
    )
end

Scripting Further: The Numbers[edit]

Each hero’s equipped weapon can be accomplished very similarly to the name. We use “get item name(string id, item)” to get the name, where item is retrieved using “check equipment(hero, slot:weapon).” get item name check equipment

plotscript, loadHud, begin
    variable(hud, txt, hero)
    
    hud := loadSliceCollection(0)
    
    for(hero, 0, 2, 1) do(
        # Name slice
        txt := lookupSlice(sli:name, sliceChild(firstChild(hud), hero))
        getHeroName(0, hero) # string ID: 0, hero slot: hero
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Item Slice
        txt := lookupSlice(sli:item, sliceChild(firstChild(hud), hero))
        getItemName(0, checkEquipment(hero, slot:weapon))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
    )
end

Storing numbers in strings is slightly different. Instead of using a command that overwrites the string with some text, we will use “append number(string id, number)” append number. Append number does not overwrite the string, however, it adds the number to the end, so we need to clear the string beforehand with “clear string(string id)” clear string.

The numbers we append are obtained from “get hero stat(hero, stat, type),” where type will be current stat or maximum stat get hero stat.

plotscript, loadHud, begin
    variable(hud, txt, hero)
    
    hud := loadSliceCollection(0)
    
    for(hero, 0, 2, 1) do(
        # Name slice
        txt := lookupSlice(sli:name, sliceChild(firstChild(hud), hero))
        getHeroName(0, hero) # string ID: 0, hero slot: hero
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Item Slice
        txt := lookupSlice(sli:item, sliceChild(firstChild(hud), hero))
        getItemName(0, checkEquipment(hero, slot:weapon))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Current HP
        txt := lookupSlice(sli:currentHp, sliceChild(firstChild(hud), hero))
        clearString(0)
        appendNumber(0, getHeroStat(hero, stat:HP, currentStat))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Max HP
        txt := lookupSlice(sli:maxHp, sliceChild(firstChild(hud), hero))
        clearString(0)
        appendNumber(0, getHeroStat(hero, stat:HP, maximumStat))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
    )
end

That’s all of the stats, set!

Finally, we can set the sprite on the HUD using “get hero picture(who, type)” get hero picture and “set sprite set number(handle, record)” set sprite set number. Using our lookup code, sli:sprite, we can get the handle for the sprite and change its set number.

plotscript, loadHud, begin
    variable(hud, txt, hero)
    
    hud := loadSliceCollection(0)
    
    for(hero, 0, 2, 1) do(
        if(heroBySlot(hero) == -1) then(continue) # skip everything after this if no hero is in this slot
        # Name slice
        txt := lookupSlice(sli:name, sliceChild(firstChild(hud), hero))
        getHeroName(0, hero) # string ID: 0, hero slot: hero
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Item Slice
        txt := lookupSlice(sli:item, sliceChild(firstChild(hud), hero))
        getItemName(0, checkEquipment(hero, slot:weapon))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Current HP
        txt := lookupSlice(sli:currentHp, sliceChild(firstChild(hud), hero))
        clearString(0)
        appendNumber(0, getHeroStat(hero, stat:HP, currentStat))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Max HP
        txt := lookupSlice(sli:maxHp, sliceChild(firstChild(hud), hero))
        clearString(0)
        appendNumber(0, getHeroStat(hero, stat:HP, maximumStat))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Sprite
        txt := lookupSlice(sli:sprite, sliceChild(firstChild(hud), hero))
        setSpriteSetNumber(txt, getHeroPicture(hero, outsideBattle))
    )
end

Updating the HUD During Gameplay[edit]

What we have now will only set the stats once, when our plotscript is called. The HUD won’t be updated when the stats change. That means we need to split our process into 2 scripts: one that loads the HUD and one that updates the HUD. So first, let’s create a new plotscript and copy everything that updates the HUD into this new script that we can call from anywhere:

plotscript, updateHud, begin
    variable(txt, hero)
    
    for(hero, 0, 2, 1) do(
        if(heroBySlot(hero) == -1) then(continue) # skip everything after this if no hero is in this slot
        # Name slice
        txt := lookupSlice(sli:name, sliceChild(firstChild(hud), hero))
        getHeroName(0, hero) # string ID: 0, hero slot: hero
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Item Slice
        txt := lookupSlice(sli:item, sliceChild(firstChild(hud), hero))
        getItemName(0, checkEquipment(hero, slot:weapon))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Current HP
        txt := lookupSlice(sli:currentHp, sliceChild(firstChild(hud), hero))
        clearString(0)
        appendNumber(0, getHeroStat(hero, stat:HP, currentStat))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Max HP
        txt := lookupSlice(sli:maxHp, sliceChild(firstChild(hud), hero))
        clearString(0)
        appendNumber(0, getHeroStat(hero, stat:HP, maximumStat))
        setSliceText(txt, 0) # text slice handle: txt, string ID: 0
        # Sprite
        txt := lookupSlice(sli:sprite, sliceChild(firstChild(hud), hero))
        setSpriteSetNumber(txt, getHeroPicture(hero, outsideBattle))
    )
end

However, because we’re in a different script, updateHud() doesn’t have access to the “hud” variable. We need the slice handle for the slice collection, and the easiest way to make this value accessible to any and all scripts is by making the hud variable “global.” This means no local variables (ones declared like “variable(hud)”) can use the name “hud,” because that name is reserved for a globally accessible variable. The result of these changes will look like this:

globalVariable(0,hud)

plotscript, loadHud, begin
    hud := loadSliceCollection(0)
    
    updateHud()
end

Note that updateHud() is called at the end of the loadHud() script to make sure the data is accurate.

Make sure to compile the scripts in Custom, then set the Each-Step and After-Battle scripts on the appropriate maps to updateHud(). Run the game and see it in action! Change hero stats, rename heroes, and swap party members around. See how it works!