Tutorial:Basic Zone Scripts: Difference between revisions

From PMDOWiki
Imbion (talk | contribs)
Created page with "PMDO has some base functions for its zones that have to be user implemented. In order to make a functioning dungeon, these functions must be filled out. From the root folder of a mod, you can find them at <code>Data/Script/[modNamespace]/zone/[zoneName]</code>. All of these functions are found in the <code>init.lua</code> file. == Init == This function is run every time the zone is initialized, such as when we enter it, exit a quicksave, or load a replay. All base dungeo..."
 
Imbion (talk | contribs)
(No difference)

Revision as of 15:46, 16 October 2025

PMDO has some base functions for its zones that have to be user implemented. In order to make a functioning dungeon, these functions must be filled out. From the root folder of a mod, you can find them at Data/Script/[modNamespace]/zone/[zoneName]. All of these functions are found in the init.lua file.

Init

This function is run every time the zone is initialized, such as when we enter it, exit a quicksave, or load a replay. All base dungeons in the game display a debug message with some information about them:

function [zone].Init(zone)
  DEBUG.EnableDbgCoro() --Enable debugging this coroutine
  PrintInfo("=>> Init_[zone]")
end

Rescued

To enable rescues, we need to fill out the Rescued function. This is rather simple, as most base dungeons just have one line of code:

function [zone].Rescued(zone, name, mail)
  COMMON.Rescued(zone, name, mail)
end

This just runs the common function for rescues found in the base game.

EnterSegment

This function runs every time a segment is entered. Generally we just want to begin the dungeon in the standard way. In that case, the function looks like as follows:

function [zone].EnterSegment(zone, rescuing, segmentID, mapID)
  if rescuing ~= true then
    COMMON.BeginDungeon(zone.ID, segmentID, mapID)
  end
end

ExitSegment

This function runs when exiting a segment. This is what most zones must fill out in order to function, as otherwise the game will simply stop execution rather than doing anything. We need to tell the game what to do upon exiting a segment through lua scripting.

This function receives the following arguments:

  • zone: The zone we currently exited.
  • result: The outcome of the dungeon exploration - Most relevant, this lets us check whenever the segment was cleared or if the player was defeated.
  • rescue: Tells us whenever we're rescuing someone or not. This is true if we are rescuing, and false if we are not.
  • segmentID: A number that tells us what segment of the dungeon we're exiting from, based on the ordering in the mod editor. It starts at 0 and counts up by one per next segment.
  • mapID: Says the map ID that we just exited from. Can be used for things like secret exits.

In order to tell the game what to do, we need to check if these variables have a certain value.

An Example

Let's take a look at how the ExitSegment function of Faded Trail works. We use a if...elseif...else block in order to branch through our different choices.

First, we want to check if we are in rescue mode, and succeeded on the rescue:

local exited = COMMON.ExitDungeonMissionCheck(result, rescue, zone.ID, segmentID)
  if exited == true then
    --do nothing

You only require this part of the block if your dungeon should support rescues.

If exited is false, then we continue along the statement. This branch is taken if the dungeon was not cleared.:

  elseif result ~= RogueEssence.Data.GameProgress.ResultType.Cleared then
    COMMON.EndDungeonDay(result, SV.checkpoint.Zone, SV.checkpoint.Segment, SV.checkpoint.Map, SV.checkpoint.Entry)

The dungeon day is ended, and the player is sent back to their last checkpoint with punishments applied.

If the above is false, then we enter into a branch that holds the results if we cleared the dungeon. So, all other results under this one else assume the player has cleared the dungeon.

Faded Trail is a dungeon with an alternative dungeon segment that the player can take. We would like to do something different if the player clears the alternative segment. The default segment, Faded Trail, is segment 0, while the alternative segment, Hidden Trail, is segment 1.

For segment 0, we just want to send the player to the forest camp:

    if segmentID == 0 then
      COMMON.EndDungeonDay(result, 'guildmaster_island', -1, 4, 0)

For segment 1, we want the player to go back to the island entrance and unlock a new dungeon:

    elseif segmentID == 1 then
      COMMON.UnlockWithFanfare('faultline_ridge', true)
      COMMON.EndDungeonDay(result, 'guildmaster_island', -1, 1, 3)

Finally, we make our last else be a fallback function that sends the player to their last checkpoint:

    else
      PrintInfo("No exit procedure found!")
	  COMMON.EndDungeonDay(result, SV.checkpoint.Zone, SV.checkpoint.Segment, SV.checkpoint.Map, SV.checkpoint.Entry)
    end

Blueprint

Putting it together, we get this psuedocode blueprint of what occurs while running ExitSegment:

function [zone].ExitSegment(zone, result, rescue, segmentID, mapID)
  DEBUG.EnableDbgCoro() --Enable debugging this coroutine
  PrintInfo("=>> ExitSegment result "..tostring(result).." segment "..tostring(segmentID))

  --first check for rescue flag; if we're in rescue mode then take a different path
  local exited = COMMON.ExitDungeonMissionCheck(result, rescue, zone.ID, segmentID) --check if we cleared while doing a rescue
  if exited == true then
        --do nothing; this is a dummy branch to stop us from going through the other branches
   elseif result ~= RogueEssence.Data.GameProgress.ResultType.Cleared then -- resolves true if the dungeon was not cleared
        --end the dungeon day by sending the player back to their last checkpoint, since they didn't clear the dungeon
         COMMON.EndDungeonDay(result, SV.checkpoint.Zone, SV.checkpoint.Segment, SV.checkpoint.Map, SV.checkpoint.Entry)
   else -- we didn't resolve true for being in rescue or losing the dungeon, so we must have cleared the dungeon
         -- now we will check what segment we took, so we can make the game do what we want to depending on the segment
         if segmentID == 0 then
            -- do things for exiting on segment 0
         elseif segmentID == 1 then
            -- do things for exiting on segment 1
         elseif .... -- continue checking likewise for all the segments we have
         else -- this is a fallback branch, in case we went through all of the branches and hit nothing (which should not happen!)
                 PrintInfo("No exit procedure found!")
                 -- just send the player to their last checkpoint
	             COMMON.EndDungeonDay(result, SV.checkpoint.Zone, SV.checkpoint.Segment, SV.checkpoint.Map, SV.checkpoint.Entry)
         end
   end
end