<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.pmdo.pmdcollab.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Palika</id>
	<title>PMDOWiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.pmdo.pmdcollab.org/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Palika"/>
	<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/Special:Contributions/Palika"/>
	<updated>2026-07-31T14:12:21Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.44.0</generator>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1975</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1975"/>
		<updated>2024-03-13T21:52:04Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These events go into the &amp;lt;code&amp;gt;event_single.lua&amp;lt;/code&amp;gt; file of your mod. As of version v0.7.25, you will need to copy this file from base PMDO into your mod first, then paste these scripts into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Custom Boss Fight Win Conditions / End effects: ==&lt;br /&gt;
:&#039;&#039;&#039;Author:&#039;&#039;&#039; Palika&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the Lua version.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of Audino&#039;s C# event for BeginBattleEvent.&lt;br /&gt;
-- This will allow us to inject custom function code before ending a battle &lt;br /&gt;
-- (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar&lt;br /&gt;
-- @tparam SingleCharContext context&lt;br /&gt;
-- @tparam table args Table of format {CustomClearEvent=&amp;quot;NameOfFunctionThatRunsOnClear&amp;quot;, [other arguments to said script]}&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Import Serpent library.&lt;br /&gt;
Serpent = require &#039;lib.serpent&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
-- Call something different from LuaBeginBattleEvent or edit this accordingly if you &lt;br /&gt;
-- want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar Not used by this function.&lt;br /&gt;
-- @tparam SingleCharContext context Not used by this function.&lt;br /&gt;
-- @tparam table args Table of caller-defined arguments. Not used by this function.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	---&lt;br /&gt;
	-- Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	-- @usage Coroutine&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		-- restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		-- heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- For each enemy team, check each chara in that team. &lt;br /&gt;
	-- If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			-- Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	-- The call originally for this was to remove(this), which isn&#039;t in lua. &lt;br /&gt;
	-- So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Usage ===&lt;br /&gt;
&lt;br /&gt;
The first function, &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, is responsible for letting PMDO know to look at &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to determine when the map is won. Instead of the usual &amp;lt;code&amp;gt;BeginBattleEvent&amp;lt;/code&amp;gt; in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:LuaBeginBattleEvent.PNG]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; is responsible for identifying when the map is won, and then the &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all; it should remain as it is. &lt;br /&gt;
&lt;br /&gt;
==== Tutorial: Alternate win condition ====&lt;br /&gt;
If you would like an alternate win condition or end sequence besides just fading out, then you will need to edit &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; and its &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; respectively. &lt;br /&gt;
&lt;br /&gt;
# First, copy and rename your custom version of &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to something more appropriately fitting. &lt;br /&gt;
#* Let&#039;s say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;. Copy the &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; function above and rename it to &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, be sure to pass an argument of &amp;lt;code&amp;gt;CustomClearEvent = &#039;CheckDefeatedGangLeader&#039;&amp;lt;/code&amp;gt; when you define it in the MapEffects, so &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt; will knows to use your custom function instead of the default one given. This is done in the Arg Table in the picture above. The value there should look something like {CustomClearEvent = &#039;CheckDefeatedGangLeader&#039;}.&lt;br /&gt;
# Next, you need to modify how &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt; identifies the win condition. If the win condition is failed, the function should return early. The default one does this by checking all enemy teams, and if any members on any team are not dead, then that means an enemy is still alive and the win condition has failed. &lt;br /&gt;
#* For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should return early.&lt;br /&gt;
#* Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &amp;lt;code&amp;gt;.GangLeader = true&amp;lt;/code&amp;gt;, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the win condition.&lt;br /&gt;
# With all this done, when the Scrafty is defeated, the map should end and call &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; as long as we aren&#039;t watching a replay. &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; is responsible for fading the map and music out.&lt;br /&gt;
#* You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation!&lt;br /&gt;
#* You can do anything of course, but be careful not to remove things that may be important to handling the end of the dungeon adventure, such as the line of code responsible for ending the segment as Cleared.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Causing a Wipe if allies faint: ==&lt;br /&gt;
&lt;br /&gt;
Want to cause a party wipe in the event that a specific character faints? Perhaps the Hero/Partner characters? This is easily achievable again via pre-made Lua scripting to be placed within event_single.lua.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll start by setting up the functionality. Place the following code within event_single.lua:&lt;br /&gt;
&lt;br /&gt;
--Check to make sure nobody marked as &amp;quot;IsPartner&amp;quot; is dead. Check guests as well&lt;br /&gt;
--if one is dead, then cause an instant game over&lt;br /&gt;
--if someone died and they aren&#039;t &amp;quot;IsPartner&amp;quot;, then send them home automatically.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.AllyDeathCheck(owner, ownerChar, context, args)&lt;br /&gt;
	local player_count = GAME:GetPlayerPartyCount()&lt;br /&gt;
	local guest_count = GAME:GetPlayerGuestCount()&lt;br /&gt;
	if player_count &amp;lt; 1 then return end--If there&#039;s no party members then we dont need to do anything&lt;br /&gt;
	for i = 0, player_count - 1, 1 do &lt;br /&gt;
		local player = GAME:GetPlayerPartyMember(i)&lt;br /&gt;
		if player.Dead and player.IsPartner then --someone died &lt;br /&gt;
			for j = 0, player_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				player = GAME:GetPlayerPartyMember(j)&lt;br /&gt;
				if not player.Dead then--dont beam out whoever died&lt;br /&gt;
					--delay between beam outs&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX))&lt;br /&gt;
					player.Dead = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			--beam out guests&lt;br /&gt;
			for j = 0, guest_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				guest = GAME:GetPlayerGuestMember(j)&lt;br /&gt;
				if not guest.Dead then--dont beam out whoever died&lt;br /&gt;
					--delay between beam outs&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX))&lt;br /&gt;
					guest.Dead = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return--cut the script short here if someone died, no need to check guests&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--check guests as well&lt;br /&gt;
	if guest_count &amp;lt; 1 then return end--If there&#039;s no guest members then we dont need to do anything&lt;br /&gt;
	for i = 0, guest_count - 1, 1 do &lt;br /&gt;
		local guest = GAME:GetPlayerGuestMember(i)&lt;br /&gt;
		if guest.Dead and guest.IsPartner then --someone died &lt;br /&gt;
			--beam player&#039;s team out first&lt;br /&gt;
			for j = 0, player_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				player = GAME:GetPlayerPartyMember(j)&lt;br /&gt;
				if not player.Dead then--dont beam out whoever died&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX))&lt;br /&gt;
					player.Dead = true&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			for j = 0, guest_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				guest = GAME:GetPlayerGuestMember(j)&lt;br /&gt;
				if not guest.Dead then--dont beam out whoever died&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX))&lt;br /&gt;
					guest.Dead = true&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once this code is in place, we need to set up Universal to call this code On Deaths. In the Dev menu, go to the Constants tab, click Universal, and scroll down to On Deaths. Expand the section there, and click Add. We&#039;ll add a SingleCharScriptEvent for AllyDeathCheck here. Once you do that, make sure you set the priority for this event to 10. This will make it so the AllyDeathCheck code we put in event_single.lua earlier will run whenever someone dies at the right timing relative to other scripts. Your config should look something like this after:&lt;br /&gt;
&lt;br /&gt;
[[File:AllyDeathUniversal.PNG]]&lt;br /&gt;
&lt;br /&gt;
You&#039;re all set now! There&#039;s only one more thing you need to do. The script we put only actually checks to see if characters marked as &amp;quot;IsPartner&amp;quot; are dead. IsPartner does not literally mean they are a partner; it is an attribute of Pokémon on your team you can set via scripting that determine whether they can leave the party or not, so it&#039;s important to give, say, the Hero character this attribute as well. However, the script code from earlier also uses that flag to determine if the character causes a party wipe upon their death. So be sure in your scripts to mark any important party members IsPartner flag to true if you want them to cause a wipe. You can, of course, change the code of the code above to check for other things instead if you want other attributes to be relevant for a party member death to cause a wipe, but IsPartner Pokémon is the most common kind of teammate you&#039;d want to cause a wipe if they faint. &lt;br /&gt;
&lt;br /&gt;
An example of setting isPartner. This would set the character in the first slot of your partner to not be removable from your team, and cause a wipe with the code from earlier if they faint.:&lt;br /&gt;
GAME:GetPlayerPartyMember(0).IsPartner = true &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Giving Exclamation points to low health allies ==&lt;br /&gt;
&lt;br /&gt;
Want that cool little effect where characters low on HP have an exclamation point over their head? Using some pre-configured event_single Lua code and by configuring your own Critical Health status, you can achieve this cosmetic effect pretty easily.&lt;br /&gt;
&lt;br /&gt;
First, let&#039;s configure the status effect that&#039;ll give the player the exclamation point over their head. Go to the Data tab on the Dev menu, then go to Statuses. Click the Add button on the menu that pops up, and it&#039;s here we&#039;ll configure our Critical Health status. Configure yours to match what you see in the image below. Anything you don&#039;t see in the window in this image can be left as the defaults.&lt;br /&gt;
&lt;br /&gt;
[[File:CriticalHealthStatus.PNG]]&lt;br /&gt;
&lt;br /&gt;
Next, insert this code into your event_single.lua file for your mod.&lt;br /&gt;
&lt;br /&gt;
Code:&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.SetCriticalHealthStatus(owner, ownerChar, context, args)&lt;br /&gt;
	local player_count = GAME:GetPlayerPartyCount()&lt;br /&gt;
	local critical = RogueEssence.Dungeon.StatusEffect(&amp;quot;critical_health&amp;quot;)&lt;br /&gt;
	--initialize status data before adding it to anything&lt;br /&gt;
	critical:LoadFromData()&lt;br /&gt;
	for i = 0, player_count - 1, 1 do &lt;br /&gt;
	local player = GAME:GetPlayerPartyMember(i)&lt;br /&gt;
		if player.HP &amp;lt;= player.MaxHP / 4 and player:GetStatusEffect(&amp;quot;critical_health&amp;quot;) == nil then &lt;br /&gt;
			TASK:WaitTask(player:AddStatusEffect(nil, critical, false))&lt;br /&gt;
		elseif player.HP &amp;gt; player.MaxHP / 4 and player:GetStatusEffect(&amp;quot;critical_health&amp;quot;) ~= nil then&lt;br /&gt;
			TASK:WaitTask(player:RemoveStatusEffect(&amp;quot;critical_health&amp;quot;))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve done that, we need to configure Universal to run this code at the end of turns. Click the Constants tab on the Dev menu, go to Universal, and scroll down until you find &amp;quot;On Turn Ends&amp;quot;. Here, we&#039;ll add a SingleCharScriptEvent that calls SetCriticalHealthStatus on the end of turns, so the Critical Health status that displays the exclamation point is set or removed when someone&#039;s HP passes the 1/4 HP threshold. Your configuration should look like this in the end:&lt;br /&gt;
&lt;br /&gt;
[[File:LowHealthExclamation.PNG]]&lt;br /&gt;
&lt;br /&gt;
You should be all set! Your ally team members (not guests, you can expand this functionality yourself if you so wish to cover guests) will get an exclamation above their head and an informative status when they&#039;re low on HP.&lt;br /&gt;
&lt;br /&gt;
[[File:LowHP.PNG]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1974</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1974"/>
		<updated>2024-03-13T21:41:54Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These events go into the &amp;lt;code&amp;gt;event_single.lua&amp;lt;/code&amp;gt; file of your mod. As of version v0.7.25, you will need to copy this file from base PMDO into your mod first, then paste these scripts into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Custom Boss Fight Win Conditions / End effects: ==&lt;br /&gt;
:&#039;&#039;&#039;Author:&#039;&#039;&#039; Palika&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the Lua version.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of Audino&#039;s C# event for BeginBattleEvent.&lt;br /&gt;
-- This will allow us to inject custom function code before ending a battle &lt;br /&gt;
-- (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar&lt;br /&gt;
-- @tparam SingleCharContext context&lt;br /&gt;
-- @tparam table args Table of format {CustomClearEvent=&amp;quot;NameOfFunctionThatRunsOnClear&amp;quot;, [other arguments to said script]}&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Import Serpent library.&lt;br /&gt;
Serpent = require &#039;lib.serpent&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
-- Call something different from LuaBeginBattleEvent or edit this accordingly if you &lt;br /&gt;
-- want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar Not used by this function.&lt;br /&gt;
-- @tparam SingleCharContext context Not used by this function.&lt;br /&gt;
-- @tparam table args Table of caller-defined arguments. Not used by this function.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	---&lt;br /&gt;
	-- Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	-- @usage Coroutine&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		-- restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		-- heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- For each enemy team, check each chara in that team. &lt;br /&gt;
	-- If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			-- Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	-- The call originally for this was to remove(this), which isn&#039;t in lua. &lt;br /&gt;
	-- So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Usage ===&lt;br /&gt;
&lt;br /&gt;
The first function, &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, is responsible for letting PMDO know to look at &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to determine when the map is won. Instead of the usual &amp;lt;code&amp;gt;BeginBattleEvent&amp;lt;/code&amp;gt; in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:LuaBeginBattleEvent.PNG]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; is responsible for identifying when the map is won, and then the &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all; it should remain as it is. &lt;br /&gt;
&lt;br /&gt;
==== Tutorial: Alternate win condition ====&lt;br /&gt;
If you would like an alternate win condition or end sequence besides just fading out, then you will need to edit &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; and its &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; respectively. &lt;br /&gt;
&lt;br /&gt;
# First, copy and rename your custom version of &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to something more appropriately fitting. &lt;br /&gt;
#* Let&#039;s say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;. Copy the &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; function above and rename it to &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, be sure to pass an argument of &amp;lt;code&amp;gt;CustomClearEvent = &#039;CheckDefeatedGangLeader&#039;&amp;lt;/code&amp;gt; when you define it in the MapEffects, so &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt; will knows to use your custom function instead of the default one given.&lt;br /&gt;
# Next, you need to modify how &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt; identifies the win condition. If the win condition is failed, the function should return early. The default one does this by checking all enemy teams, and if any members on any team are not dead, then that means an enemy is still alive and the win condition has failed. &lt;br /&gt;
#* For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should return early.&lt;br /&gt;
#* Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &amp;lt;code&amp;gt;.GangLeader = true&amp;lt;/code&amp;gt;, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the win condition.&lt;br /&gt;
# With all this done, when the Scrafty is defeated, the map should end and call &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; as long as we aren&#039;t watching a replay. &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; is responsible for fading the map and music out.&lt;br /&gt;
#* You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation!&lt;br /&gt;
#* You can do anything of course, but be careful not to remove things that may be important to handling the end of the dungeon adventure, such as the line of code responsible for ending the segment as Cleared.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Causing a Wipe if allies faint: ==&lt;br /&gt;
&lt;br /&gt;
Want to cause a party wipe in the event that a specific character faints? Perhaps the Hero/Partner characters? This is easily achievable again via pre-made Lua scripting to be placed within event_single.lua.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll start by setting up the functionality. Place the following code within event_single.lua:&lt;br /&gt;
&lt;br /&gt;
--Check to make sure nobody marked as &amp;quot;IsPartner&amp;quot; is dead. Check guests as well&lt;br /&gt;
--if one is dead, then cause an instant game over&lt;br /&gt;
--if someone died and they aren&#039;t &amp;quot;IsPartner&amp;quot;, then send them home automatically.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.AllyDeathCheck(owner, ownerChar, context, args)&lt;br /&gt;
	local player_count = GAME:GetPlayerPartyCount()&lt;br /&gt;
	local guest_count = GAME:GetPlayerGuestCount()&lt;br /&gt;
	if player_count &amp;lt; 1 then return end--If there&#039;s no party members then we dont need to do anything&lt;br /&gt;
	for i = 0, player_count - 1, 1 do &lt;br /&gt;
		local player = GAME:GetPlayerPartyMember(i)&lt;br /&gt;
		if player.Dead and player.IsPartner then --someone died &lt;br /&gt;
			for j = 0, player_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				player = GAME:GetPlayerPartyMember(j)&lt;br /&gt;
				if not player.Dead then--dont beam out whoever died&lt;br /&gt;
					--delay between beam outs&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX))&lt;br /&gt;
					player.Dead = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			--beam out guests&lt;br /&gt;
			for j = 0, guest_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				guest = GAME:GetPlayerGuestMember(j)&lt;br /&gt;
				if not guest.Dead then--dont beam out whoever died&lt;br /&gt;
					--delay between beam outs&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX))&lt;br /&gt;
					guest.Dead = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return--cut the script short here if someone died, no need to check guests&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--check guests as well&lt;br /&gt;
	if guest_count &amp;lt; 1 then return end--If there&#039;s no guest members then we dont need to do anything&lt;br /&gt;
	for i = 0, guest_count - 1, 1 do &lt;br /&gt;
		local guest = GAME:GetPlayerGuestMember(i)&lt;br /&gt;
		if guest.Dead and guest.IsPartner then --someone died &lt;br /&gt;
			--beam player&#039;s team out first&lt;br /&gt;
			for j = 0, player_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				player = GAME:GetPlayerPartyMember(j)&lt;br /&gt;
				if not player.Dead then--dont beam out whoever died&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX))&lt;br /&gt;
					player.Dead = true&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			for j = 0, guest_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				guest = GAME:GetPlayerGuestMember(j)&lt;br /&gt;
				if not guest.Dead then--dont beam out whoever died&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX))&lt;br /&gt;
					guest.Dead = true&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once this code is in place, we need to set up Universal to call this code On Deaths. In the Dev menu, go to the Constants tab, click Universal, and scroll down to On Deaths. Expand the section there, and click Add. We&#039;ll add a SingleCharScriptEvent for AllyDeathCheck here. Once you do that, make sure you set the priority for this event to 10. This will make it so the AllyDeathCheck code we put in event_single.lua earlier will run whenever someone dies at the right timing relative to other scripts. Your config should look something like this after:&lt;br /&gt;
&lt;br /&gt;
[[File:AllyDeathUniversal.PNG]]&lt;br /&gt;
&lt;br /&gt;
You&#039;re all set now! There&#039;s only one more thing you need to do. The script we put only actually checks to see if characters marked as &amp;quot;IsPartner&amp;quot; are dead. IsPartner does not literally mean they are a partner; it is an attribute of Pokémon on your team you can set via scripting that determine whether they can leave the party or not, so it&#039;s important to give, say, the Hero character this attribute as well. However, the script code from earlier also uses that flag to determine if the character causes a party wipe upon their death. So be sure in your scripts to mark any important party members IsPartner flag to true if you want them to cause a wipe. You can, of course, change the code of the code above to check for other things instead if you want other attributes to be relevant for a party member death to cause a wipe, but IsPartner Pokémon is the most common kind of teammate you&#039;d want to cause a wipe if they faint. &lt;br /&gt;
&lt;br /&gt;
An example of setting isPartner. This would set the character in the first slot of your partner to not be removable from your team, and cause a wipe with the code from earlier if they faint.:&lt;br /&gt;
GAME:GetPlayerPartyMember(0).IsPartner = true &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Giving Exclamation points to low health allies ==&lt;br /&gt;
&lt;br /&gt;
Want that cool little effect where characters low on HP have an exclamation point over their head? Using some pre-configured event_single Lua code and by configuring your own Critical Health status, you can achieve this cosmetic effect pretty easily.&lt;br /&gt;
&lt;br /&gt;
First, let&#039;s configure the status effect that&#039;ll give the player the exclamation point over their head. Go to the Data tab on the Dev menu, then go to Statuses. Click the Add button on the menu that pops up, and it&#039;s here we&#039;ll configure our Critical Health status. Configure yours to match what you see in the image below. Anything you don&#039;t see in the window in this image can be left as the defaults.&lt;br /&gt;
&lt;br /&gt;
[[File:CriticalHealthStatus.PNG]]&lt;br /&gt;
&lt;br /&gt;
Next, insert this code into your event_single.lua file for your mod.&lt;br /&gt;
&lt;br /&gt;
Code:&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.SetCriticalHealthStatus(owner, ownerChar, context, args)&lt;br /&gt;
	local player_count = GAME:GetPlayerPartyCount()&lt;br /&gt;
	local critical = RogueEssence.Dungeon.StatusEffect(&amp;quot;critical_health&amp;quot;)&lt;br /&gt;
	--initialize status data before adding it to anything&lt;br /&gt;
	critical:LoadFromData()&lt;br /&gt;
	for i = 0, player_count - 1, 1 do &lt;br /&gt;
	local player = GAME:GetPlayerPartyMember(i)&lt;br /&gt;
		if player.HP &amp;lt;= player.MaxHP / 4 and player:GetStatusEffect(&amp;quot;critical_health&amp;quot;) == nil then &lt;br /&gt;
			TASK:WaitTask(player:AddStatusEffect(nil, critical, false))&lt;br /&gt;
		elseif player.HP &amp;gt; player.MaxHP / 4 and player:GetStatusEffect(&amp;quot;critical_health&amp;quot;) ~= nil then&lt;br /&gt;
			TASK:WaitTask(player:RemoveStatusEffect(&amp;quot;critical_health&amp;quot;))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve done that, we need to configure Universal to run this code at the end of turns. Click the Constants tab on the Dev menu, go to Universal, and scroll down until you find &amp;quot;On Turn Ends&amp;quot;. Here, we&#039;ll add a SingleCharScriptEvent that calls SetCriticalHealthStatus on the end of turns, so the Critical Health status that displays the exclamation point is set or removed when someone&#039;s HP passes the 1/4 HP threshold. Your configuration should look like this in the end:&lt;br /&gt;
&lt;br /&gt;
[[File:LowHealthExclamation.PNG]]&lt;br /&gt;
&lt;br /&gt;
You should be all set! Your ally team members (not guests, you can expand this functionality yourself if you so wish to cover guests) will get an exclamation above their head and an informative status when they&#039;re low on HP.&lt;br /&gt;
&lt;br /&gt;
[[File:LowHP.PNG]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:AllyDeathUniversal.PNG&amp;diff=1973</id>
		<title>File:AllyDeathUniversal.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:AllyDeathUniversal.PNG&amp;diff=1973"/>
		<updated>2024-03-13T21:35:28Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1972</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1972"/>
		<updated>2024-03-13T21:25:44Z</updated>

		<summary type="html">&lt;p&gt;Palika: added crit HP status stuff&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These events go into the &amp;lt;code&amp;gt;event_single.lua&amp;lt;/code&amp;gt; file of your mod. As of version v0.7.25, you will need to copy this file from base PMDO into your mod first, then paste these scripts into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Custom Boss Fight Win Conditions / End effects: ==&lt;br /&gt;
:&#039;&#039;&#039;Author:&#039;&#039;&#039; Palika&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the Lua version.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of Audino&#039;s C# event for BeginBattleEvent.&lt;br /&gt;
-- This will allow us to inject custom function code before ending a battle &lt;br /&gt;
-- (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar&lt;br /&gt;
-- @tparam SingleCharContext context&lt;br /&gt;
-- @tparam table args Table of format {CustomClearEvent=&amp;quot;NameOfFunctionThatRunsOnClear&amp;quot;, [other arguments to said script]}&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Import Serpent library.&lt;br /&gt;
Serpent = require &#039;lib.serpent&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
-- Call something different from LuaBeginBattleEvent or edit this accordingly if you &lt;br /&gt;
-- want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar Not used by this function.&lt;br /&gt;
-- @tparam SingleCharContext context Not used by this function.&lt;br /&gt;
-- @tparam table args Table of caller-defined arguments. Not used by this function.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	---&lt;br /&gt;
	-- Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	-- @usage Coroutine&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		-- restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		-- heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- For each enemy team, check each chara in that team. &lt;br /&gt;
	-- If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			-- Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	-- The call originally for this was to remove(this), which isn&#039;t in lua. &lt;br /&gt;
	-- So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Usage ===&lt;br /&gt;
&lt;br /&gt;
The first function, &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, is responsible for letting PMDO know to look at &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to determine when the map is won. Instead of the usual &amp;lt;code&amp;gt;BeginBattleEvent&amp;lt;/code&amp;gt; in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:LuaBeginBattleEvent.PNG]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; is responsible for identifying when the map is won, and then the &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all; it should remain as it is. &lt;br /&gt;
&lt;br /&gt;
==== Tutorial: Alternate win condition ====&lt;br /&gt;
If you would like an alternate win condition or end sequence besides just fading out, then you will need to edit &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; and its &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; respectively. &lt;br /&gt;
&lt;br /&gt;
# First, copy and rename your custom version of &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to something more appropriately fitting. &lt;br /&gt;
#* Let&#039;s say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;. Copy the &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; function above and rename it to &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, be sure to pass an argument of &amp;lt;code&amp;gt;CustomClearEvent = &#039;CheckDefeatedGangLeader&#039;&amp;lt;/code&amp;gt; when you define it in the MapEffects, so &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt; will knows to use your custom function instead of the default one given.&lt;br /&gt;
# Next, you need to modify how &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt; identifies the win condition. If the win condition is failed, the function should return early. The default one does this by checking all enemy teams, and if any members on any team are not dead, then that means an enemy is still alive and the win condition has failed. &lt;br /&gt;
#* For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should return early.&lt;br /&gt;
#* Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &amp;lt;code&amp;gt;.GangLeader = true&amp;lt;/code&amp;gt;, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the win condition.&lt;br /&gt;
# With all this done, when the Scrafty is defeated, the map should end and call &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; as long as we aren&#039;t watching a replay. &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; is responsible for fading the map and music out.&lt;br /&gt;
#* You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation!&lt;br /&gt;
#* You can do anything of course, but be careful not to remove things that may be important to handling the end of the dungeon adventure, such as the line of code responsible for ending the segment as Cleared.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Causing a Wipe if allies faint: ==&lt;br /&gt;
&lt;br /&gt;
Want to cause a party wipe in the event that a specific character faints? Perhaps the Hero/Partner characters? This is easily achievable again via pre-made Lua scripting to be placed within event_single.lua.&lt;br /&gt;
&lt;br /&gt;
--Check to make sure nobody marked as &amp;quot;IsPartner&amp;quot; is dead. Check guests as well&lt;br /&gt;
--if one is dead, then cause an instant game over&lt;br /&gt;
--if someone died and they aren&#039;t &amp;quot;IsPartner&amp;quot;, then send them home automatically.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.AllyDeathCheck(owner, ownerChar, context, args)&lt;br /&gt;
	local player_count = GAME:GetPlayerPartyCount()&lt;br /&gt;
	local guest_count = GAME:GetPlayerGuestCount()&lt;br /&gt;
	if player_count &amp;lt; 1 then return end--If there&#039;s no party members then we dont need to do anything&lt;br /&gt;
	for i = 0, player_count - 1, 1 do &lt;br /&gt;
		local player = GAME:GetPlayerPartyMember(i)&lt;br /&gt;
		if player.Dead and player.IsPartner then --someone died &lt;br /&gt;
			for j = 0, player_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				player = GAME:GetPlayerPartyMember(j)&lt;br /&gt;
				if not player.Dead then--dont beam out whoever died&lt;br /&gt;
					--delay between beam outs&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX))&lt;br /&gt;
					player.Dead = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			--beam out guests&lt;br /&gt;
			for j = 0, guest_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				guest = GAME:GetPlayerGuestMember(j)&lt;br /&gt;
				if not guest.Dead then--dont beam out whoever died&lt;br /&gt;
					--delay between beam outs&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX))&lt;br /&gt;
					guest.Dead = true&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return--cut the script short here if someone died, no need to check guests&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--check guests as well&lt;br /&gt;
	if guest_count &amp;lt; 1 then return end--If there&#039;s no guest members then we dont need to do anything&lt;br /&gt;
	for i = 0, guest_count - 1, 1 do &lt;br /&gt;
		local guest = GAME:GetPlayerGuestMember(i)&lt;br /&gt;
		if guest.Dead and guest.IsPartner then --someone died &lt;br /&gt;
			--beam player&#039;s team out first&lt;br /&gt;
			for j = 0, player_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				player = GAME:GetPlayerPartyMember(j)&lt;br /&gt;
				if not player.Dead then--dont beam out whoever died&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(player, player, _DATA.SendHomeFX))&lt;br /&gt;
					player.Dead = true&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			for j = 0, guest_count - 1, 1 do --beam everyone else out&lt;br /&gt;
				guest = GAME:GetPlayerGuestMember(j)&lt;br /&gt;
				if not guest.Dead then--dont beam out whoever died&lt;br /&gt;
					TASK:WaitTask(_DUNGEON:ProcessBattleFX(guest, guest, _DATA.SendHomeFX))&lt;br /&gt;
					guest.Dead = true&lt;br /&gt;
					GAME:WaitFrames(60)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
			&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Giving Exclamation points to low health allies ==&lt;br /&gt;
&lt;br /&gt;
Want that cool little effect where characters low on HP have an exclamation point over their head? Using some pre-configured event_single Lua code and by configuring your own Critical Health status, you can achieve this cosmetic effect pretty easily.&lt;br /&gt;
&lt;br /&gt;
First, let&#039;s configure the status effect that&#039;ll give the player the exclamation point over their head. Go to the Data tab on the Dev menu, then go to Statuses. Click the Add button on the menu that pops up, and it&#039;s here we&#039;ll configure our Critical Health status. Configure yours to match what you see in the image below. Anything you don&#039;t see in the window in this image can be left as the defaults.&lt;br /&gt;
&lt;br /&gt;
[[File:CriticalHealthStatus.PNG]]&lt;br /&gt;
&lt;br /&gt;
Next, insert this code into your event_single.lua file for your mod.&lt;br /&gt;
&lt;br /&gt;
Code:&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.SetCriticalHealthStatus(owner, ownerChar, context, args)&lt;br /&gt;
	local player_count = GAME:GetPlayerPartyCount()&lt;br /&gt;
	local critical = RogueEssence.Dungeon.StatusEffect(&amp;quot;critical_health&amp;quot;)&lt;br /&gt;
	--initialize status data before adding it to anything&lt;br /&gt;
	critical:LoadFromData()&lt;br /&gt;
	for i = 0, player_count - 1, 1 do &lt;br /&gt;
	local player = GAME:GetPlayerPartyMember(i)&lt;br /&gt;
		if player.HP &amp;lt;= player.MaxHP / 4 and player:GetStatusEffect(&amp;quot;critical_health&amp;quot;) == nil then &lt;br /&gt;
			TASK:WaitTask(player:AddStatusEffect(nil, critical, false))&lt;br /&gt;
		elseif player.HP &amp;gt; player.MaxHP / 4 and player:GetStatusEffect(&amp;quot;critical_health&amp;quot;) ~= nil then&lt;br /&gt;
			TASK:WaitTask(player:RemoveStatusEffect(&amp;quot;critical_health&amp;quot;))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Once you&#039;ve done that, we need to configure Universal to run this code at the end of turns. Click the Constants tab on the Dev menu, go to Universal, and scroll down until you find &amp;quot;On Turn Ends&amp;quot;. Here, we&#039;ll add a SingleCharScriptEvent that calls SetCriticalHealthStatus on the end of turns, so the Critical Health status that displays the exclamation point is set or removed when someone&#039;s HP passes the 1/4 HP threshold. Your configuration should look like this in the end:&lt;br /&gt;
&lt;br /&gt;
[[File:LowHealthExclamation.PNG]]&lt;br /&gt;
&lt;br /&gt;
You should be all set! Your ally team members (not guests, you can expand this functionality yourself if you so wish to cover guests) will get an exclamation above their head and an informative status when they&#039;re low on HP.&lt;br /&gt;
&lt;br /&gt;
[[File:LowHP.PNG]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:LowHP.PNG&amp;diff=1971</id>
		<title>File:LowHP.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:LowHP.PNG&amp;diff=1971"/>
		<updated>2024-03-13T21:25:19Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:LowHealthExclamation.PNG&amp;diff=1970</id>
		<title>File:LowHealthExclamation.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:LowHealthExclamation.PNG&amp;diff=1970"/>
		<updated>2024-03-13T21:23:10Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:CriticalHealthStatus.PNG&amp;diff=1969</id>
		<title>File:CriticalHealthStatus.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:CriticalHealthStatus.PNG&amp;diff=1969"/>
		<updated>2024-03-13T21:19:50Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1968</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1968"/>
		<updated>2024-03-13T20:43:23Z</updated>

		<summary type="html">&lt;p&gt;Palika: added serpent import&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These events go into the &amp;lt;code&amp;gt;event_single.lua&amp;lt;/code&amp;gt; file of your mod. As of version v0.7.25, you will need to copy this file from base PMDO into your mod first, then paste these scripts into it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Custom Boss Fight Win Conditions / End effects: ==&lt;br /&gt;
:&#039;&#039;&#039;Author:&#039;&#039;&#039; Palika&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the Lua version.&lt;br /&gt;
&lt;br /&gt;
=== Code ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of Audino&#039;s C# event for BeginBattleEvent.&lt;br /&gt;
-- This will allow us to inject custom function code before ending a battle &lt;br /&gt;
-- (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar&lt;br /&gt;
-- @tparam SingleCharContext context&lt;br /&gt;
-- @tparam table args Table of format {CustomClearEvent=&amp;quot;NameOfFunctionThatRunsOnClear&amp;quot;, [other arguments to said script]}&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Import Serpent library.&lt;br /&gt;
Serpent = require &#039;lib.serpent&#039;&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
-- Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
-- Call something different from LuaBeginBattleEvent or edit this accordingly if you &lt;br /&gt;
-- want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
-- @tparam GameEventOwner owner&lt;br /&gt;
-- @tparam Character ownerChar Not used by this function.&lt;br /&gt;
-- @tparam SingleCharContext context Not used by this function.&lt;br /&gt;
-- @tparam table args Table of caller-defined arguments. Not used by this function.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	local MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
	local SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
	&lt;br /&gt;
	---&lt;br /&gt;
	-- Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	-- @usage Coroutine&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		-- restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		-- heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- For each enemy team, check each chara in that team. &lt;br /&gt;
	-- If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			-- Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	-- The call originally for this was to remove(this), which isn&#039;t in lua. &lt;br /&gt;
	-- So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Usage ===&lt;br /&gt;
&lt;br /&gt;
The first function, &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, is responsible for letting PMDO know to look at &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to determine when the map is won. Instead of the usual &amp;lt;code&amp;gt;BeginBattleEvent&amp;lt;/code&amp;gt; in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:LuaBeginBattleEvent.PNG]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; is responsible for identifying when the map is won, and then the &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all; it should remain as it is. &lt;br /&gt;
&lt;br /&gt;
==== Tutorial: Alternate win condition ====&lt;br /&gt;
If you would like an alternate win condition or end sequence besides just fading out, then you will need to edit &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; and its &amp;lt;code&amp;gt;end_sequence&amp;lt;/code&amp;gt; respectively. &lt;br /&gt;
&lt;br /&gt;
# First, copy and rename your custom version of &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; to something more appropriately fitting. &lt;br /&gt;
#* Let&#039;s say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;. Copy the &amp;lt;code&amp;gt;LuaCheckBossClearEvent&amp;lt;/code&amp;gt; function above and rename it to &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt;.&lt;br /&gt;
# In &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt;, be sure to pass an argument of &amp;lt;code&amp;gt;CustomClearEvent = &#039;CheckDefeatedGangLeader&#039;&amp;lt;/code&amp;gt; when you define it in the MapEffects, so &amp;lt;code&amp;gt;LuaBeginBattleEvent&amp;lt;/code&amp;gt; will knows to use your custom function instead of the default one given.&lt;br /&gt;
# Next, you need to modify how &amp;lt;code&amp;gt;CheckDefeatedGangLeader&amp;lt;/code&amp;gt; identifies the win condition. If the win condition is failed, the function should return early. The default one does this by checking all enemy teams, and if any members on any team are not dead, then that means an enemy is still alive and the win condition has failed. &lt;br /&gt;
#* For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should return early.&lt;br /&gt;
#* Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &amp;lt;code&amp;gt;&#039;GangLeader&#039; = true&amp;lt;/code&amp;gt;, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the win condition.&lt;br /&gt;
# With all this done, when the Scrafty is defeated, the map should end and call &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; as long as we aren&#039;t watching a replay. &amp;lt;code&amp;gt;end_sequence()&amp;lt;/code&amp;gt; is responsible for fading the map and music out.&lt;br /&gt;
#* You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation!&lt;br /&gt;
#* You can do anything of course, but be careful not to remove things that may be important to handling the end of the dungeon adventure, such as the line of code responsible for ending the segment as Cleared.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1966</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1966"/>
		<updated>2024-03-13T19:12:23Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Custom Boss Fight Win Conditions / End effects:&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the lua version.&lt;br /&gt;
&lt;br /&gt;
Luckily for you, the C# version of it has been reimplemented into Lua already for you. All you need to do is take the below code and copy and paste them into event_single.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Reimplementing Audino&#039;s C# event for BeginBattleEvent. &lt;br /&gt;
--This will allow us to inject custom function code before ending a battle (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
--Call something different from LuaBeginBattleEvent or edit this accordingly if you want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	&lt;br /&gt;
	--Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		--restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		--heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--For each enemy team, check each chara in that team. If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			--Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	--The call originally for this was to remove(this), which isn&#039;t in lua. So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The first function, LuaBeginBattleEvent is responsible for letting PMDO know to look at LuaCheckBossClearEvent to determine when the map is won. Instead of the usual BeginBattleEvent in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:LuaBeginBattleEvent.PNG]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LuaCheckBossClearEvent is responsible for identifying when the map is won, and then the end_sequence function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all, it should remain as it is. If you need an alternate wincon/end sequence besides just fading out, then you need to edit LuaCheckBossClearEvent and its end_sequence respectively. &lt;br /&gt;
&lt;br /&gt;
First thing&#039;s first, unless you want to be lazy, you should rename your custom version of LuaCheckBossClearEvent to something more appropriately fitting. Let&#039;s say we want a win con where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead CheckDefeatedGangLeader. Take the LuaCheckBossClearEvent and all its code, and just rename it to CheckDefeatedGangLeader.&lt;br /&gt;
&lt;br /&gt;
Now, in LuaBeginBattleEvent, be sure to pass an argument of CustomClearEvent = &#039;CheckDefeatedGangLeader&#039; when you define it in the MapEffects so the LuaBeginBattleEvent knows to use your custom function instead of the default one given.&lt;br /&gt;
&lt;br /&gt;
Next, you need to modify how CheckDefeatedGangLeader identifies the wincon. You should write it in a way that has the function return early if the win condition is failed. The default one does this by checking all enemy teams, and if any members on any team is not dead, then that means an enemy is still alive and the win condition has failed.&lt;br /&gt;
&lt;br /&gt;
For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should end early. Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &#039;GangLeader&#039; = true, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the wincon.&lt;br /&gt;
&lt;br /&gt;
Now, with all this out of the way, when the Scrafty is defeated, the map should end and call end_sequence() (as long as we aren&#039;t watching a replay). end_sequence() is responsible for fading the map and music out. You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation! You can do anything of course, but be careful not to remove things that are important unless you know what you&#039;re doing, such as the line of code responsible for ending the segment as Cleared.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1965</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1965"/>
		<updated>2024-03-13T19:11:53Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Custom Boss Fight Win Conditions / End effects:&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the lua version.&lt;br /&gt;
&lt;br /&gt;
Luckily for you, the C# version of it has been reimplemented into Lua already for you. All you need to do is take the below code and copy and paste them into event_single.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Reimplementing Audino&#039;s C# event for BeginBattleEvent. &lt;br /&gt;
--This will allow us to inject custom function code before ending a battle (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
--Call something different from LuaBeginBattleEvent or edit this accordingly if you want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	&lt;br /&gt;
	--Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		--restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		--heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--For each enemy team, check each chara in that team. If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			--Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	--The call originally for this was to remove(this), which isn&#039;t in lua. So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The first function, LuaBeginBattleEvent is responsible for letting PMDO know to look at LuaCheckBossClearEvent to determine when the map is won. Instead of the usual BeginBattleEvent in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:LuaBeginBattleEvent.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LuaCheckBossClearEvent is responsible for identifying when the map is won, and then the end_sequence function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all, it should remain as it is. If you need an alternate wincon/end sequence besides just fading out, then you need to edit LuaCheckBossClearEvent and its end_sequence respectively. &lt;br /&gt;
&lt;br /&gt;
First thing&#039;s first, unless you want to be lazy, you should rename your custom version of LuaCheckBossClearEvent to something more appropriately fitting. Let&#039;s say we want a win con where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead CheckDefeatedGangLeader. Take the LuaCheckBossClearEvent and all its code, and just rename it to CheckDefeatedGangLeader.&lt;br /&gt;
&lt;br /&gt;
Now, in LuaBeginBattleEvent, be sure to pass an argument of CustomClearEvent = &#039;CheckDefeatedGangLeader&#039; when you define it in the MapEffects so the LuaBeginBattleEvent knows to use your custom function instead of the default one given.&lt;br /&gt;
&lt;br /&gt;
Next, you need to modify how CheckDefeatedGangLeader identifies the wincon. You should write it in a way that has the function return early if the win condition is failed. The default one does this by checking all enemy teams, and if any members on any team is not dead, then that means an enemy is still alive and the win condition has failed.&lt;br /&gt;
&lt;br /&gt;
For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should end early. Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &#039;GangLeader&#039; = true, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the wincon.&lt;br /&gt;
&lt;br /&gt;
Now, with all this out of the way, when the Scrafty is defeated, the map should end and call end_sequence() (as long as we aren&#039;t watching a replay). end_sequence() is responsible for fading the map and music out. You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation! You can do anything of course, but be careful not to remove things that are important unless you know what you&#039;re doing, such as the line of code responsible for ending the segment as Cleared.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1964</id>
		<title>SingleEvent Scripts</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=SingleEvent_Scripts&amp;diff=1964"/>
		<updated>2024-03-13T19:11:19Z</updated>

		<summary type="html">&lt;p&gt;Palika: Created page with &amp;quot; Custom Boss Fight Win Conditions / End effects:  Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&amp;#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the lua version.  Luckily for you, the C# version of it has been reimplemented into Lua already f...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Custom Boss Fight Win Conditions / End effects:&lt;br /&gt;
&lt;br /&gt;
Want to have it so a boss fight has an alternative win condition than just &amp;quot;beat all enemies&amp;quot;? Want something special to happen when that win condition is met? If the answer is yes, you&#039;ll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the lua version.&lt;br /&gt;
&lt;br /&gt;
Luckily for you, the C# version of it has been reimplemented into Lua already for you. All you need to do is take the below code and copy and paste them into event_single.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Reimplementing Audino&#039;s C# event for BeginBattleEvent. &lt;br /&gt;
--This will allow us to inject custom function code before ending a battle (such as clearing existing lava flows when a boss fight ends).&lt;br /&gt;
MapCheckState = luanet.import_type(&#039;RogueEssence.Dungeon.MapCheckState&#039;)&lt;br /&gt;
SingleCharScriptEvent = luanet.import_type(&#039;RogueEssence.Dungeon.SingleCharScriptEvent&#039;)&lt;br /&gt;
&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args)&lt;br /&gt;
	&lt;br /&gt;
	local map_clear_idx = &#039;map_clear_check&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
	if context.User ~= nil then return end&lt;br /&gt;
	--if a custom clear event is not given, use the default one.&lt;br /&gt;
	if args.CustomClearEvent == nil then args.CustomClearEvent = &#039;LuaCheckBossClearEvent&#039; end&lt;br /&gt;
	&lt;br /&gt;
	--Turn on Team Mode if allowed when the boss fight starts.&lt;br /&gt;
	if _DUNGEON:CanUseTeamMode() then&lt;br /&gt;
		_DUNGEON:SetTeamMode(true)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx)&lt;br /&gt;
	clear_status:LoadFromData()&lt;br /&gt;
	&lt;br /&gt;
	local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	--The 2nd argument in the function below needs a string that represents a lua table of the arguments to pass. Serpent.line will convert the lua table to a string representing it for us.&lt;br /&gt;
	--We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called&lt;br /&gt;
	check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args)))&lt;br /&gt;
	--check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args))&lt;br /&gt;
	&lt;br /&gt;
	TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--Reimplementation of the basic CheckBossClearEvent. &lt;br /&gt;
--Call something different from LuaBeginBattleEvent or edit this accordingly if you want a different wincon for your map or special effects/anims on win.&lt;br /&gt;
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args)&lt;br /&gt;
	&lt;br /&gt;
	--Sequence that runs when map is over. Fade out, cut the music, etc.&lt;br /&gt;
	function end_sequence()&lt;br /&gt;
		_GAME:BGM(&amp;quot;&amp;quot;, true)&lt;br /&gt;
		 &lt;br /&gt;
		TASK:WaitTask(_GAME:FadeOut(false))&lt;br /&gt;
		 &lt;br /&gt;
		_DUNGEON:ResetTurns()&lt;br /&gt;
		&lt;br /&gt;
		--restore all and remove all map status&lt;br /&gt;
		local statuses_to_remove = {}&lt;br /&gt;
		for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do&lt;br /&gt;
			statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i]&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for i = 0, #statuses_to_remove - 1, 1 do&lt;br /&gt;
			TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false))&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		--heal everyone in the party&lt;br /&gt;
		for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do&lt;br /&gt;
			_DATA.Save.ActiveTeam.Players[i]:FullRestore()&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--For each enemy team, check each chara in that team. If any are still alive, then fail this check and return early.&lt;br /&gt;
	for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do &lt;br /&gt;
		local team = _ZONE.CurrentMap.MapTeams[i].Players&lt;br /&gt;
		for j = 0, team.Count - 1, 1 do&lt;br /&gt;
			--Break and return early if even one enemy is not dead.&lt;br /&gt;
			if not team[j].Dead then return end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	--Everyone&#039;s dead, clear the scene.&lt;br /&gt;
	local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState))&lt;br /&gt;
	&lt;br /&gt;
	--The call originally for this was to remove(this), which isn&#039;t in lua. So we need to find the LuaCheckBossClearEvent and remove that (remove ourself)&lt;br /&gt;
	for i = 0, checks.CheckEvents.Count - 1, 1 do&lt;br /&gt;
		if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then&lt;br /&gt;
			if checks.CheckEvents[i].Script == args.CustomClearEvent then&lt;br /&gt;
				checks.CheckEvents:Remove(checks.CheckEvents[i])&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if _DATA.CurrentReplay == nil then&lt;br /&gt;
		TASK:WaitTask(end_sequence())&lt;br /&gt;
	else &lt;br /&gt;
		TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The first function, LuaBeginBattleEvent is responsible for letting PMDO know to look at LuaCheckBossClearEvent to determine when the map is won. Instead of the usual BeginBattleEvent in your boss fight&#039;s OnMapStart&#039;s Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It&#039;ll look like this:&lt;br /&gt;
&lt;br /&gt;
[[File:&amp;lt;LuaBeginBattleEvent&amp;gt;.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
LuaCheckBossClearEvent is responsible for identifying when the map is won, and then the end_sequence function within it is responsible for fading stuff out and actually winning the map. &lt;br /&gt;
&lt;br /&gt;
You won&#039;t need to modify the scripting for LuaBeginBattleEvent at all, it should remain as it is. If you need an alternate wincon/end sequence besides just fading out, then you need to edit LuaCheckBossClearEvent and its end_sequence respectively. &lt;br /&gt;
&lt;br /&gt;
First thing&#039;s first, unless you want to be lazy, you should rename your custom version of LuaCheckBossClearEvent to something more appropriately fitting. Let&#039;s say we want a win con where you only have to defeat the leader of a gang to win the boss fight. We&#039;ll call this function instead CheckDefeatedGangLeader. Take the LuaCheckBossClearEvent and all its code, and just rename it to CheckDefeatedGangLeader.&lt;br /&gt;
&lt;br /&gt;
Now, in LuaBeginBattleEvent, be sure to pass an argument of CustomClearEvent = &#039;CheckDefeatedGangLeader&#039; when you define it in the MapEffects so the LuaBeginBattleEvent knows to use your custom function instead of the default one given.&lt;br /&gt;
&lt;br /&gt;
Next, you need to modify how CheckDefeatedGangLeader identifies the wincon. You should write it in a way that has the function return early if the win condition is failed. The default one does this by checking all enemy teams, and if any members on any team is not dead, then that means an enemy is still alive and the win condition has failed.&lt;br /&gt;
&lt;br /&gt;
For our example, if our gang leader is a Scrafty, we should iterate through all enemy teams and see if we find a member that is both a Scrafty and alive. If so, then our win condition has failed and we should end early. Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, &#039;GangLeader&#039; = true, and make sure that there are no NPCs with a LuaTable property of &#039;GangLeader&#039; equal to true that are alive. Doing it like this would allow you to have as many GangLeaders as you may want that need to be defeated to meet the wincon.&lt;br /&gt;
&lt;br /&gt;
Now, with all this out of the way, when the Scrafty is defeated, the map should end and call end_sequence() (as long as we aren&#039;t watching a replay). end_sequence() is responsible for fading the map and music out. You can redefine it to anything you want if you want different things to happen when you actually win. For example, maybe you want your party members to face the camera and do a pose for a few seconds before the game fades out. You could accomplish this by writing code at the very top of end_sequence that makes all your party members do the face down and do the Pose animation! You can do anything of course, but be careful not to remove things that are important unless you know what you&#039;re doing, such as the line of code responsible for ending the segment as Cleared.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:LuaBeginBattleEvent.PNG&amp;diff=1963</id>
		<title>File:LuaBeginBattleEvent.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:LuaBeginBattleEvent.PNG&amp;diff=1963"/>
		<updated>2024-03-13T18:55:18Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1917</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1917"/>
		<updated>2023-12-23T21:12:24Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will initialize the ground character you spawned in (known internally as &#039;Teammate1&#039;) with the partner following AI, targeted at following the player character. Then, it&#039;ll disable their collision, as otherwise you&#039;ll get stuck on them constantly. &lt;br /&gt;
&lt;br /&gt;
Assuming your init script was fresh before starting, it&#039;ll look something like this afterwards:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnAllies()&lt;br /&gt;
  local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, whenever the map loads, the character in slot 2 of the party will follow you around that map! You&#039;ll need to set this up for every map you want the partner to follow you around in.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Following.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if the player/partner isn&#039;t in the party, but I want them to be the overworld Pokemon? ===&lt;br /&gt;
To do this, you&#039;ll have to set things up a bit differently. You&#039;ll want to call your spawner PARTNER_SPAWN and have the spawned entity called &amp;quot;Partner&amp;quot;, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner_Method_2.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll need to adjust the values in Scriptvars&#039;s test_grounds data to be the player/partner you want. You&#039;ll see in the screenshot below variables that correspond to the player/partner&#039;s appearance. &lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Scriptvars.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
-Species = lower case string of the species of Pokemon.&lt;br /&gt;
-Form = The number for the form of the mon. This should be 0, unless you want to use a non default form of a pokemon (for example, Hisuian Zorua). Then, you need to change that to the number that corresponds to that form, which you can figure out with the dropdown in dev menu&#039;s Player tab. Just make sure you have the correct species selected first.&lt;br /&gt;
-skin = should be &amp;quot;normal&amp;quot; or &amp;quot;shiny&amp;quot;, but can be other things if your mod includes additional skin types&lt;br /&gt;
-Gender = Set to 0 for Genderless, 1 for male, and 2 for female.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll do the rest of this the same as before, but you&#039;ll replace the COMMON.RespawnAllies call with   COMMON.RespawnStarterPartner(). Additionally, you&#039;ll need to refer to the partner as &amp;quot;Partner&amp;quot; rather than &amp;quot;Teammate1&amp;quot;. So you&#039;ll end up with:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnStarterPartner()&lt;br /&gt;
  local partner = CH(&#039;Partner&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And presto, even though my party consists of the Kanto starters, in the overworld I am a Pikachu and an Eevee.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Party_Mismatch.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Of course, you&#039;re free to reimplement these functions to better suit your needs (or to have variables with better names), but the quick and dirty way is to reuse the functions provided by base PMDO&#039;s COMMON functionality as described above. Just be sure to read the code for the functions you&#039;re using above to get a grasp of how they work first.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if my map has multiple entrances? ===&lt;br /&gt;
The partner can only spawn in one place, and that&#039;s where the spawner is placed on the map. If you want them to appear next to you when you enter a hub world map from one of its many entrances, you&#039;ll need to add logic that gets triggered in the Enter callback for that map that teleports them to the player&#039;s side while the game is still faded out. You can do this in a number of ways, but one such way would be to have a variable get updated when leaving one map and going to the map with multiple entrances that lets the multiple entrance map&#039;s script know where to teleport the partner to. &lt;br /&gt;
&lt;br /&gt;
For example, Halcyon has a system for this where a variable keeps track of the marker to teleport the partner to on the next map when a different map is exited. The partner gets teleported to this marker when the new map loads but before it fades in. The object responsible for map transitions on the different maps is what needs to update this variable, mind you, so if you want to see an example of this, be sure to check those map transition objects.&lt;br /&gt;
&lt;br /&gt;
=== My partner doesn&#039;t spawn behind me when I save and reload! Why? ===&lt;br /&gt;
The ground map by default doesn&#039;t discriminate between loading in via a map transition or loading in via loading a saved game. You&#039;ll need to introduce your own logic to handle this. One such way is adding a function to the GameSave callback that saves to Scriptvars the position and direction of the partner. Then, add another function to the GameLoad callback that teleports the partner to the position and direction that was saved earlier. Halcyon has an example of this you can look at or reuse if you&#039;d like.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1916</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1916"/>
		<updated>2023-12-23T21:11:22Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will initialize the ground character you spawned in (known internally as &#039;Teammate1&#039;) with the partner following AI, targeted at following the player character. Then, it&#039;ll disable their collision, as otherwise you&#039;ll get stuck on them constantly. &lt;br /&gt;
&lt;br /&gt;
Assuming your init script was fresh before starting, it&#039;ll look something like this afterwards:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnAllies()&lt;br /&gt;
  local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, whenever the map loads, the character in slot 2 of the party will follow you around that map! You&#039;ll need to set this up for every map you want the partner to follow you around in.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Following.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if the player/partner isn&#039;t in the party, but I want them to be the overworld Pokemon? ===&lt;br /&gt;
To do this, you&#039;ll have to set things up a bit differently. You&#039;ll want to call your spawner PARTNER_SPAWN and have the spawned entity called &amp;quot;Partner&amp;quot;, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner_Method_2.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll need to adjust the values in Scriptvars&#039;s test_grounds data to be the player/partner you want. You&#039;ll see in the screenshot below variables that correspond to the player/partner&#039;s appearance. &lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Scriptvars.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
-Species = lower case string of the species of Pokemon.&lt;br /&gt;
-Form = The number for the form of the mon. This should be 0, unless you want to use a non default form of a pokemon (for example, Hisuian Zorua). Then, you need to change that to the number that corresponds to that form, which you can figure out with the dropdown in dev menu&#039;s Player tab. Just make sure you have the correct species selected first.&lt;br /&gt;
-skin = should be &amp;quot;normal&amp;quot; or &amp;quot;shiny&amp;quot;, but can be other things if your mod includes additional skin types&lt;br /&gt;
-Gender = Set to 0 for Genderless, 1 for male, and 2 for female.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll do the rest of this the same as before, but you&#039;ll replace the COMMON.RespawnAllies call with   COMMON.RespawnStarterPartner(). Additionally, you&#039;ll need to refer to the partner as &amp;quot;Partner&amp;quot; rather than &amp;quot;Teammate1&amp;quot;. So you&#039;ll end up with:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnStarterPartner()&lt;br /&gt;
  local partner = CH(&#039;Partner&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And presto, even though my party consists of the Kanto starters, in the overworld I am a Pikachu and an Eevee.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Mismatch.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Of course, you&#039;re free to reimplement these functions to better suit your needs (or to have variables with better names), but the quick and dirty way is to reuse the functions provided by base PMDO&#039;s COMMON functionality as described above. Just be sure to read the code for the functions you&#039;re using above to get a grasp of how they work first.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if my map has multiple entrances? ===&lt;br /&gt;
The partner can only spawn in one place, and that&#039;s where the spawner is placed on the map. If you want them to appear next to you when you enter a hub world map from one of its many entrances, you&#039;ll need to add logic that gets triggered in the Enter callback for that map that teleports them to the player&#039;s side while the game is still faded out. You can do this in a number of ways, but one such way would be to have a variable get updated when leaving one map and going to the map with multiple entrances that lets the multiple entrance map&#039;s script know where to teleport the partner to. &lt;br /&gt;
&lt;br /&gt;
For example, Halcyon has a system for this where a variable keeps track of the marker to teleport the partner to on the next map when a different map is exited. The partner gets teleported to this marker when the new map loads but before it fades in. The object responsible for map transitions on the different maps is what needs to update this variable, mind you, so if you want to see an example of this, be sure to check those map transition objects.&lt;br /&gt;
&lt;br /&gt;
=== My partner doesn&#039;t spawn behind me when I save and reload! Why? ===&lt;br /&gt;
The ground map by default doesn&#039;t discriminate between loading in via a map transition or loading in via loading a saved game. You&#039;ll need to introduce your own logic to handle this. One such way is adding a function to the GameSave callback that saves to Scriptvars the position and direction of the partner. Then, add another function to the GameLoad callback that teleports the partner to the position and direction that was saved earlier. Halcyon has an example of this you can look at or reuse if you&#039;d like.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1915</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1915"/>
		<updated>2023-12-23T21:11:03Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will initialize the ground character you spawned in (known internally as &#039;Teammate1&#039;) with the partner following AI, targeted at following the player character. Then, it&#039;ll disable their collision, as otherwise you&#039;ll get stuck on them constantly. &lt;br /&gt;
&lt;br /&gt;
Assuming your init script was fresh before starting, it&#039;ll look something like this afterwards:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnAllies()&lt;br /&gt;
  local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, whenever the map loads, the character in slot 2 of the party will follow you around that map! You&#039;ll need to set this up for every map you want the partner to follow you around in.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Following.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if the player/partner isn&#039;t in the party, but I want them to be the overworld Pokemon? ===&lt;br /&gt;
To do this, you&#039;ll have to set things up a bit differently. You&#039;ll want to call your spawner PARTNER_SPAWN and have the spawned entity called &amp;quot;Partner&amp;quot;, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner_Method_2.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll need to adjust the values in Scriptvars&#039;s test_grounds data to be the player/partner you want. You&#039;ll see in the screenshot below variables that correspond to the player/partner&#039;s appearance. &lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Scriptvars.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
-Species = lower case string of the species of Pokemon.&lt;br /&gt;
-Form = The number for the form of the mon. This should be 0, unless you want to use a non default form of a pokemon (for example, Hisuian Zorua). Then, you need to change that to the number that corresponds to that form, which you can figure out with the dropdown in dev menu&#039;s Player tab. Just make sure you have the correct species selected first.&lt;br /&gt;
-skin = should be &amp;quot;normal&amp;quot; or &amp;quot;shiny&amp;quot;, but can be other things if your mod includes additional skin types&lt;br /&gt;
-Gender = Set to 0 for Genderless, 1 for male, and 2 for female.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll do the rest of this the same as before, but you&#039;ll replace the COMMON.RespawnAllies call with   COMMON.RespawnStarterPartner(). Additionally, you&#039;ll need to refer to the partner as &amp;quot;Partner&amp;quot; rather than &amp;quot;Teammate1&amp;quot;. So you&#039;ll end up with:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnStarterPartner()&lt;br /&gt;
  local partner = CH(&#039;Partner&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And presto, even though my party consists of the Kanto starters, in the overworld I am a Pikachu and an Eevee.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Mismatch.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Of course, you&#039;re free to reimplement these functions to better suit your needs (or to have variables with better names), but the quick and dirty way is to reuse the functions provided by base PMDO&#039;s COMMON functionality as described above. Just be sure to read the code for the functions you&#039;re using above to get a grasp of how they work first.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if my map has multiple entrances? ===&lt;br /&gt;
The partner can only spawn in one place, and that&#039;s where the spawner is placed on the map. If you want them to appear next to you when you enter a hub world map from one of its many entrances, you&#039;ll need to add logic that gets triggered in the Enter callback for that map that teleports them to the player&#039;s side while the game is still faded out. You can do this in a number of ways, but one such way would be to have a variable get updated when leaving one map and going to the map with multiple entrances that lets the multiple entrance map&#039;s script know where to teleport the partner to. &lt;br /&gt;
&lt;br /&gt;
For example, Halcyon has a system for this where a variable keeps track of the marker to teleport the partner to on the next map when a different map is exited. The partner gets teleported to this marker when the new map loads but before it fades in. The object responsible for map transitions on the different maps is what needs to update this variable, mind you, so if you want to see an example of this, be sure to check those map transition objects.&lt;br /&gt;
&lt;br /&gt;
=== My partner doesn&#039;t spawn behind me when I save and reload! Why? ===&lt;br /&gt;
The ground map by default doesn&#039;t discriminate between loading in via a map transition or loading in via loading a saved game. You&#039;ll need to introduce your own logic to handle this. One such way is adding a function to the GameSave callback that saves to Scriptvars the position and direction of the partner. Then, add another function to the GameLoad callback that teleports the partner to the position and direction that was saved earlier. Halcyon has an example of this you can look at or reuse if you&#039;d like.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Spawner_Method_2.PNG&amp;diff=1914</id>
		<title>File:Partner Spawner Method 2.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Spawner_Method_2.PNG&amp;diff=1914"/>
		<updated>2023-12-23T21:06:31Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category: Tutorial Images]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Party_Mismatch.PNG&amp;diff=1913</id>
		<title>File:Partner Party Mismatch.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Party_Mismatch.PNG&amp;diff=1913"/>
		<updated>2023-12-23T21:06:30Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category: Tutorial Images]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Scriptvars.PNG&amp;diff=1912</id>
		<title>File:Partner Scriptvars.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Scriptvars.PNG&amp;diff=1912"/>
		<updated>2023-12-23T21:06:30Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category: Tutorial Images]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Following.PNG&amp;diff=1911</id>
		<title>File:Partner Following.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Following.PNG&amp;diff=1911"/>
		<updated>2023-12-23T21:06:30Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category: Tutorial Images]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1910</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1910"/>
		<updated>2023-12-23T21:04:24Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will initialize the ground character you spawned in (known internally as &#039;Teammate1&#039;) with the partner following AI, targeted at following the player character. Then, it&#039;ll disable their collision, as otherwise you&#039;ll get stuck on them constantly. &lt;br /&gt;
&lt;br /&gt;
Assuming your init script was fresh before starting, it&#039;ll look something like this afterwards:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnAllies()&lt;br /&gt;
  local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, whenever the map loads, the character in slot 2 of the party will follow you around that map! You&#039;ll need to set this up for every map you want the partner to follow you around in.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Following.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if the player/partner isn&#039;t in the party, but I want them to be the overworld Pokemon? ===&lt;br /&gt;
To do this, you&#039;ll have to set things up a bit differently. You&#039;ll want to call your spawner PARTNER_SPAWN and have the spawned entity called &amp;quot;Partner&amp;quot;, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner_Method_2.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Next, you&#039;ll need to adjust the values in Scriptvars&#039;s test_grounds data to be the player/partner you want. You&#039;ll see in the screenshot below variables that correspond to the player/partner&#039;s appearance. &lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Scriptvars.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
-Species = lower case string of the species of Pokemon.&lt;br /&gt;
-Form = The number for the form of the mon. This should be 0, unless you want to use a non default form of a pokemon (for example, Hisuian Zorua). Then, you need to change that to the number that corresponds to that form, which you can figure out with the dropdown in dev menu&#039;s Player tab. Just make sure you have the correct species selected first.&lt;br /&gt;
-skin = should be &amp;quot;normal&amp;quot; or &amp;quot;shiny&amp;quot;, but can be other things if your mod includes additional skin types&lt;br /&gt;
-Gender = Set to 0 for Genderless, 1 for male, and 2 for female.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll do the rest of this the same as before, but you&#039;ll replace the COMMON.RespawnAllies call with   COMMON.RespawnStarterPartner(). Additionally, you&#039;ll need to refer to the partner as &amp;quot;Partner&amp;quot; rather than &amp;quot;Teammate1&amp;quot;. So you&#039;ll end up with:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnStarterPartner()&lt;br /&gt;
  local partner = CH(&#039;Partner&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And presto, even though my party consists of the Kanto starters, in the overworld I am a Pikachu and an Eevee.&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Mismatch.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Of course, you&#039;re free to reimplement these functions to better suit your needs (or to have variables with better names), but the quick and dirty way is to reuse the functions provided by base PMDO&#039;s COMMON functionality as described above. Just be sure to read the code for the functions you&#039;re using above to get a grasp of how they work first.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if my map has multiple entrances? ===&lt;br /&gt;
The partner can only spawn in one place, and that&#039;s where the spawner is placed on the map. If you want them to appear next to you when you enter a hub world map from one of its many entrances, you&#039;ll need to add logic that gets triggered in the Enter callback for that map that teleports them to the player&#039;s side while the game is still faded out. You can do this in a number of ways, but one such way would be to have a variable get updated when leaving one map and going to the map with multiple entrances that lets the multiple entrance map&#039;s script know where to teleport the partner to. &lt;br /&gt;
&lt;br /&gt;
For example, Halcyon has a system for this where a variable keeps track of the marker to teleport the partner to on the next map when a different map is exited. The partner gets teleported to this marker when the new map loads but before it fades in. The object responsible for map transitions on the different maps is what needs to update this variable, mind you, so if you want to see an example of this, be sure to check those map transition objects.&lt;br /&gt;
&lt;br /&gt;
=== My partner doesn&#039;t spawn behind me when I save and reload! Why? ===&lt;br /&gt;
The ground map by default doesn&#039;t discriminate between loading in via a map transition or loading in via loading a saved game. You&#039;ll need to introduce your own logic to handle this. One such way is adding a function to the GameSave callback that saves to Scriptvars the position and direction of the partner. Then, add another function to the GameLoad callback that teleports the partner to the position and direction that was saved earlier. Halcyon has an example of this you can look at or reuse if you&#039;d like.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1909</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1909"/>
		<updated>2023-12-23T20:50:46Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will initialize the ground character you spawned in (known internally as &#039;Teammate1&#039;) with the partner following AI, targeted at following the player character. Then, it&#039;ll disable their collision, as otherwise you&#039;ll get stuck on them constantly. &lt;br /&gt;
&lt;br /&gt;
Assuming your init script was fresh before starting, it&#039;ll look something like this afterwards:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnAllies()&lt;br /&gt;
  local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, whenever the map loads, the character in slot 2 of the party will follow you around that map! You&#039;ll need to set this up for every map you want the partner to follow you around in.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if the player/partner isn&#039;t in the party, but I want them to be the overworld Pokemon? ===&lt;br /&gt;
To do this, you&#039;ll have to set things up a bit differently.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if my map has multiple entrances? ===&lt;br /&gt;
The partner can only spawn in one place, and that&#039;s where the spawner is placed on the map. If you want them to appear next to you when you enter a hub world map from one of its many entrances, you&#039;ll need to add logic that gets triggered in the Enter callback for that map that teleports them to the player&#039;s side while the game is still faded out. You can do this in a number of ways, but one such way would be to have a variable get updated when leaving one map and going to the map with multiple entrances that lets the multiple entrance map&#039;s script know where to teleport the partner to. &lt;br /&gt;
&lt;br /&gt;
For example, Halcyon has a system for this where a variable keeps track of the marker to teleport the partner to on the next map when a different map is exited. The partner gets teleported to this marker when the new map loads but before it fades in. The object responsible for map transitions on the different maps is what needs to update this variable, mind you, so if you want to see an example of this, be sure to check those map transition objects.&lt;br /&gt;
&lt;br /&gt;
=== My partner doesn&#039;t spawn behind me when I save and reload! Why? ===&lt;br /&gt;
The ground map by default doesn&#039;t discriminate between loading in via a map transition or loading in via loading a saved game. You&#039;ll need to introduce your own logic to handle this. One such way is adding a function to the GameSave callback that saves to Scriptvars the position and direction of the partner. Then, add another function to the GameLoad callback that teleports the partner to the position and direction that was saved earlier. Halcyon has an example of this you can look at or reuse if you&#039;d like.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1908</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1908"/>
		<updated>2023-12-23T20:41:05Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will initialize the ground character you spawned in (known internally as &#039;Teammate1&#039;) with the partner following AI, targeted at following the player character. Then, it&#039;ll disable their collision, as otherwise you&#039;ll get stuck on them constantly. &lt;br /&gt;
&lt;br /&gt;
Assuming your init script was fresh before starting, it&#039;ll look something like this afterwards:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function YourGroundName.Init(map)&lt;br /&gt;
  MapStrings = COMMON.AutoLoadLocalizedStrings()&lt;br /&gt;
  COMMON.RespawnAllies()&lt;br /&gt;
  local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
  AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
  partner.CollisionDisabled = true&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, whenever the map loads, the character in slot 2 of the party will follow you around that map! You&#039;ll need to set this up for every map you want the partner to follow you around in.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== What if the player/partner isn&#039;t in the party, but I want them to be the overworld Pokemon? ===&lt;br /&gt;
To do this, you&#039;ll have to set things up a bit differently.&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1907</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1907"/>
		<updated>2023-12-23T20:34:29Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. It&#039;ll also call spawners named TEAMMATE_2 or TEAMMATE_3 if they exist, if you want to have your other party members appear on the map. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1906</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1906"/>
		<updated>2023-12-23T20:33:48Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback function. Here, you&#039;ll need to add several functions related to spawning in and setting up your partner.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to start with calling COMMON.RespawnAllies(). This will call the spawner you made earlier. It&#039;ll also call spawners named TEAMMATE_2 or TEAMMATE_3 if they exist, if you want to have your other party members appear on the map. From here, you&#039;ll need to set up your partner&#039;s data and AI. You can do this with the following commands:&lt;br /&gt;
&lt;br /&gt;
local partner = CH(&#039;Teammate1&#039;)&lt;br /&gt;
AI:SetcharacterAI(partner, &#039;ai.ground_partner&#039;, CH(&#039;PLAYER&#039;), partner.Position)&lt;br /&gt;
partner.CollisionDisabled = true&lt;br /&gt;
&lt;br /&gt;
This will&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1905</id>
		<title>Tutorial:Partner Following</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Partner_Following&amp;diff=1905"/>
		<updated>2023-12-23T20:12:56Z</updated>

		<summary type="html">&lt;p&gt;Palika: Created page with &amp;quot;Want your partner to follow you around in the overworld? It&amp;#039;ll take some scripting work as well as map editor work to achieve this. Let&amp;#039;s start with the map editor.  You&amp;#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&amp;#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&amp;#039;ll call the spawner TEAMMATE_1 an...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Want your partner to follow you around in the overworld? It&#039;ll take some scripting work as well as map editor work to achieve this. Let&#039;s start with the map editor.&lt;br /&gt;
&lt;br /&gt;
You&#039;ll need to place down a spawner that the script will use to spawn in your partner. You can do this in the Entities tab, and choosing an object type of spawner. Let&#039;s work with the assumption that your partner will be whoever is in slot 2 of your party. In such a case, we&#039;ll call the spawner TEAMMATE_1 and the entity it spawns Teammate1, as seen below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Partner_Spawner.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Place the spawner wherever in the map you want the partner to spawn in. You can also set the default direction they spawn in as by changing the direction of the spawner. Once this is done, save your map changes. It&#039;s now time to make changes to the init script for the ground so the game knows what to do with that spawner.&lt;br /&gt;
&lt;br /&gt;
Open up your init script and look at the Init callback for&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Spawner.PNG&amp;diff=1904</id>
		<title>File:Partner Spawner.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Partner_Spawner.PNG&amp;diff=1904"/>
		<updated>2023-12-23T20:10:17Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1383</id>
		<title>Tutorial:Custom Floor Generation Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1383"/>
		<updated>2023-07-07T00:11:29Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&#039;ll walk through an example on how this was done for Halcyon&#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on what exactly you&#039;re trying to achieve with your custom gen step, you may want to start with the vanilla gen steps to get a base to work off of, or you may want to start with the Lua customization first. Either way, once you&#039;re ready to sit down and work with the Lua scripting, there&#039;s some basic functions/knowledge you&#039;ll need to know. There are more functions beyond these, but these are all we need for creating a river. You may find additional functions and tools to do things such as creating traps, items, or NPCs in the FLOOR_GEN_SCRIPT.Test function in event_mapgen.lua.&lt;br /&gt;
&lt;br /&gt;
The function that defines your custom gen function should be given two parameters: map and args.&lt;br /&gt;
*Map is the floor you&#039;re on that you&#039;re generating. It includes a number of useful functions and variables such as:&lt;br /&gt;
**map.Width: Width of the map&lt;br /&gt;
**map.Height: Height of the map&lt;br /&gt;
**map.Rand:Next(x, y) - returns a random number, inclusive on lower and exclusive on upper, between x and y. This is important to use instead of math.random so that the dungeon&#039;s random seeding is used to generate the random number so replays and such are consistent.&lt;br /&gt;
**map:GetTile(loc) - gets the tile at the specified RogueElements.Loc(x,y).&lt;br /&gt;
**map:TrySetTile(loc, terrain) - attempts to set the tile at the specified location to the specified terrain type. May fail if the tile should not be changed for some reason (for example, it is the loc of the stairs, or is out of bounds)&lt;br /&gt;
    &lt;br /&gt;
*Args is a list of arguments given in the Arg table in the ScriptGenStep. Using these, you can write somewhat generic generation algorithms that can be tweaked on a per floor basis by giving different arguments to the function via the genstep&#039;s arg table depending on what floor you&#039;re on. For example, if I wanted the river to be bigger on later floors, I could supply an Arg called RiverSize that the function could use by grabbing it from the Args table.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, you&#039;ll need to figure out a way to programmatically define your generation step. In our case, we need to figure out how we want to define what constitutes a river. For what Halcyon wanted, the following attributes were needed for the river:&lt;br /&gt;
*It takes up about half the map, with its center being around the middle of the dungeon&#039;s X axis.&lt;br /&gt;
*It flows vertically.&lt;br /&gt;
*Its border randomly shifts left and right a few tiles every so often, but not so far as to shift too far from the center.&lt;br /&gt;
*Its border never shifts 2 water tiles at a time, or else it may look jagged and strange.&lt;br /&gt;
*The left and right borders work independently of each other.&lt;br /&gt;
*It doesn&#039;t override ground tiles, only wall tiles. This way rooms and hallways are not changed into water.&lt;br /&gt;
&lt;br /&gt;
And with that, we use this and our knowledge of the Lua functions to define an algorithm in event_mapgen.lua that will change wall tiles near the middle of the map into water tiles to simulate a river. Below is some Lua that&#039;ll do just that, but you&#039;ll need to write something different if you want to do something else:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--Halcyon custom map gen steps&lt;br /&gt;
--used for making the river in the Illuminant Riverbed&lt;br /&gt;
function FLOOR_GEN_SCRIPT.CreateRiver(map, args)&lt;br /&gt;
	local mapCenter = math.ceil(map.Width / 2)&lt;br /&gt;
	local randomOffset = map.Rand:Next(-2,3) --a random small offset added to all tiles to help randomize where the river falls a bit &lt;br /&gt;
	local leftBound = math.floor(mapCenter / 2) + randomOffset --base left bound &lt;br /&gt;
	local rightBound = math.ceil(mapCenter # 3 / 2) + randomOffset -- base right bound&lt;br /&gt;
	local leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local leftShore = 0&lt;br /&gt;
	local rightShore = 0&lt;br /&gt;
	&lt;br /&gt;
	local leftOffsetRemaining = map.Rand:Next(1, 5)--how many times this specific offset can be used before being regenerated &lt;br /&gt;
	local rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	--go row by row. Replace ground tiles towards the center of the map with water tiles to create a river flowing through the dungeon.&lt;br /&gt;
	--Ground tiles will remain untouched. River will ebb a bit side to side within a limit.&lt;br /&gt;
	&lt;br /&gt;
	for y = 0, map.Height-1, 1 do &lt;br /&gt;
		&lt;br /&gt;
		--determine starting and ending positions for row of river&lt;br /&gt;
		--an offset will last for a few rows before trying to roll again for a new offset&lt;br /&gt;
		&lt;br /&gt;
		--roll new offsets and set new offset timer &lt;br /&gt;
		--NOTE: map.Rand:Next(lower, upper) is inclusive on lower, and exclusive on upper &lt;br /&gt;
		if leftOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if leftOffset &amp;lt; 0 then&lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif leftOffset &amp;gt; 0 then &lt;br /&gt;
				leftOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			leftOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if rightOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if rightOffset &amp;lt; 0 then&lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif rightOffset &amp;gt; 0 then &lt;br /&gt;
				rightOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		leftShore = leftBound + leftOffset&lt;br /&gt;
		rightShore = rightBound + rightOffset&lt;br /&gt;
				&lt;br /&gt;
		--set all non ground tiles to water tiles between our left and right bounds &lt;br /&gt;
		for x = leftShore, rightShore, 1 do &lt;br /&gt;
			local loc = RogueElements.Loc(x, y)&lt;br /&gt;
			if not map:GetTile(loc):TileEquivalent(map.RoomTerrain) then&lt;br /&gt;
				map:TrySetTile(loc, RogueEssence.Dungeon.Tile(&amp;quot;water&amp;quot;))&lt;br /&gt;
			end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		leftOffsetRemaining = leftOffsetRemaining - 1&lt;br /&gt;
		rightOffsetRemaining = rightOffsetRemaining - 1&lt;br /&gt;
		&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now that we have our function defined, we need to actually add it to the dungeon floor! This is done by adding it to the gen steps for the floor in the dungeon setup. Where you place it in orderwise depends on what your genstep does, but in our case, it&#039;s something that should be ran after all other terrain generation has finished, so we&#039;ll put it at priority 4 here. For the Script area, we write the name of our script (in this case, CreateRiver), and define any arguments we may have (in this case, none).&lt;br /&gt;
&lt;br /&gt;
[[File:Custom Gen Step Instructions.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Then, we just load up our floor, and voila! We have a simple but decent looking river now flowing through our dungeon floor!&lt;br /&gt;
&lt;br /&gt;
[[File:2sBVFyb-1-.png|frameless]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1382</id>
		<title>Tutorial:Custom Floor Generation Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1382"/>
		<updated>2023-07-07T00:04:16Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&#039;ll walk through an example on how this was done for Halcyon&#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on what exactly you&#039;re trying to achieve with your custom gen step, you may want to start with the vanilla gen steps to get a base to work off of, or you may want to start with the Lua customization first. Either way, once you&#039;re ready to sit down and work with the Lua scripting, there&#039;s some basic functions/knowledge you&#039;ll need to know. There are more functions beyond these, but these are all we need for creating a river. You may find additional functions and tools to do things such as creating traps, items, or NPCs in the FLOOR_GEN_SCRIPT.Test function in event_mapgen.lua.&lt;br /&gt;
&lt;br /&gt;
The function that defines your custom gen function should be given two parameters: map and args.&lt;br /&gt;
*Map is the floor you&#039;re on that you&#039;re generating. It includes a number of useful functions and variables such as:&lt;br /&gt;
**map.Width: Width of the map&lt;br /&gt;
**map.Height: Height of the map&lt;br /&gt;
**map.Rand:Next(x, y) - returns a random number, inclusive on lower and exclusive on upper, between x and y. This is important to use instead of math.random so that the dungeon&#039;s random seeding is used to generate the random number so replays and such are consistent.&lt;br /&gt;
**map:GetTile(loc) - gets the tile at the specified RogueElements.Loc(x,y).&lt;br /&gt;
**map:TrySetTile(loc, terrain) - attempts to set the tile at the specified location to the specified terrain type. May fail if the tile should not be changed for some reason (for example, it is the loc of the stairs)&lt;br /&gt;
    &lt;br /&gt;
*Args is a list of arguments given in the Arg Table in the ScriptGenStep. Using these, you can write somewhat generic generation algorithms that can be tweaked on a per floor basis by giving different arguments to the function via the genstep&#039;s arg table!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, we need to figure out how we&#039;d want to go about defining what constitutes our river. In this case, we want the following attributes for our river:&lt;br /&gt;
*It takes up about half the map, with its center being around the middle of the dungeon&#039;s X axis.&lt;br /&gt;
*It flows vertically.&lt;br /&gt;
*Its border randomly shifts left and right a few tiles every so often, but not so far as to shift too far from the center.&lt;br /&gt;
*Its border never shifts 2 water tiles at a time, or else it may look jagged and strange.&lt;br /&gt;
*It doesn&#039;t override ground tiles, only wall tiles. This way rooms and hallways are not changed into water.&lt;br /&gt;
&lt;br /&gt;
And with that, we use this and our knowledge of the lua functions to define a function in event_mapgen.lua that will change wall tiles near the middle of the map into water tiles to simulate a river:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Halcyon custom map gen steps&lt;br /&gt;
--used for making the river in the Illuminant Riverbed&lt;br /&gt;
function FLOOR_GEN_SCRIPT.CreateRiver(map, args)&lt;br /&gt;
	local mapCenter = math.ceil(map.Width / 2)&lt;br /&gt;
	local randomOffset = map.Rand:Next(-2,3) --a random small offset added to all tiles to help randomize where the river falls a bit &lt;br /&gt;
	local leftBound = math.floor(mapCenter / 2) + randomOffset --base left bound &lt;br /&gt;
	local rightBound = math.ceil(mapCenter # 3 / 2) + randomOffset -- base right bound&lt;br /&gt;
	local leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local leftShore = 0&lt;br /&gt;
	local rightShore = 0&lt;br /&gt;
	&lt;br /&gt;
	local leftOffsetRemaining = map.Rand:Next(1, 5)--how many times this specific offset can be used before being regenerated &lt;br /&gt;
	local rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	--go row by row. Replace ground tiles towards the center of the map with water tiles to create a river flowing through the dungeon.&lt;br /&gt;
	--Ground tiles will remain untouched. River will ebb a bit side to side within a limit.&lt;br /&gt;
	&lt;br /&gt;
	for y = 0, map.Height-1, 1 do &lt;br /&gt;
		&lt;br /&gt;
		--determine starting and ending positions for row of river&lt;br /&gt;
		--an offset will last for a few rows before trying to roll again for a new offset&lt;br /&gt;
		&lt;br /&gt;
		--roll new offsets and set new offset timer &lt;br /&gt;
		--NOTE: map.Rand:Next(lower, upper) is inclusive on lower, and exclusive on upper &lt;br /&gt;
		if leftOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if leftOffset &amp;lt; 0 then&lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif leftOffset &amp;gt; 0 then &lt;br /&gt;
				leftOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			leftOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if rightOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if rightOffset &amp;lt; 0 then&lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif rightOffset &amp;gt; 0 then &lt;br /&gt;
				rightOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		leftShore = leftBound + leftOffset&lt;br /&gt;
		rightShore = rightBound + rightOffset&lt;br /&gt;
				&lt;br /&gt;
		--set all non ground tiles to water tiles between our left and right bounds &lt;br /&gt;
		for x = leftShore, rightShore, 1 do &lt;br /&gt;
			local loc = RogueElements.Loc(x, y)&lt;br /&gt;
			if not map:GetTile(loc):TileEquivalent(map.RoomTerrain) then&lt;br /&gt;
				map:TrySetTile(loc, RogueEssence.Dungeon.Tile(&amp;quot;water&amp;quot;))&lt;br /&gt;
			end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		leftOffsetRemaining = leftOffsetRemaining - 1&lt;br /&gt;
		rightOffsetRemaining = rightOffsetRemaining - 1&lt;br /&gt;
		&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Now that we have our function defined, we need to actually add it to the dungeon floor! This is done by adding it to the gen steps for the floor in the dungeon setup. Where you place it in orderwise depends on what your genstep does, but in our case, it&#039;s something that should be ran after all other terrain generation has finished, so we&#039;ll put it at priority 4 here. For the Script area, we write the name of our script (in this case, CreateRiver), and define any arguments we may have (in this case, none).&lt;br /&gt;
&lt;br /&gt;
[[File:Custom Gen Step Instructions.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Then, we just load up our floor, and voila! We have a simple but decent looking river now flowing through our dungeon floor!&lt;br /&gt;
&lt;br /&gt;
[[File:2sBVFyb-1-.png|frameless]]&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1373</id>
		<title>Tutorial:Custom Floor Generation Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1373"/>
		<updated>2023-07-06T18:11:50Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&#039;ll walk through an example on how this was done for Halcyon&#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on what exactly you&#039;re trying to achieve with your custom gen step, you may want to start with the vanilla gen steps to get a base to work off of, or you may want to start with the Lua customization first. Either way, once you&#039;re ready to sit down and work with the Lua scripting, there&#039;s some basic functions/knowledge you&#039;ll need to know. There are more functions beyond these, but these are all we need for creating a river. You may find additional functions and tools to do things such as creating traps, items, or NPCs in the FLOOR_GEN_SCRIPT.Test function in event_mapgen.lua.&lt;br /&gt;
&lt;br /&gt;
The function that defines your custom gen function should be given two parameters: map and args.&lt;br /&gt;
Map is the floor you&#039;re on that you&#039;re generating. It includes a number of useful functions and variables such as:&lt;br /&gt;
  * map.Width: Width of the map&lt;br /&gt;
  * map.Height: Height of the map&lt;br /&gt;
  * map.Rand:Next(x, y) - returns a random number, inclusive on lower and exclusive on upper, between x and y. This is important to use instead of math.random so that the dungeon&#039;s random seeding is used to generate the random number so replays and such are consistent.&lt;br /&gt;
  * map:GetTile(loc) - gets the tile at the specified RogueElements.Loc(x,y).&lt;br /&gt;
  * map:TrySetTile(loc, terrain) - attempts to set the tile at the specified location to the specified terrain type. May fail if the tile should not be changed for some reason (for example, it is the loc of the stairs)&lt;br /&gt;
    &lt;br /&gt;
Args is a list of arguments given in the Arg Table in the ScriptGenStep. Using these, you can write somewhat generic generation algorithms that can be tweaked on a per floor basis by giving different arguments to the function via the genstep&#039;s arg table!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, we need to figure out how we&#039;d want to go about defining what constitutes our river. In this case, we want the following attributes for our river:&lt;br /&gt;
  * It takes up about half the map, with its center being around the middle of the dungeon&#039;s X axis.&lt;br /&gt;
  * It flows vertically.&lt;br /&gt;
  * Its border randomly shifts left and right a few tiles every so often, but not so far as to shift too far from the center.&lt;br /&gt;
  * Its border never shifts 2 water tiles at a time, or else it may look jagged and strange.&lt;br /&gt;
  * It doesn&#039;t override ground tiles, only wall tiles. This way rooms and hallways are not changed into water.&lt;br /&gt;
&lt;br /&gt;
And with that, we use this and our knowledge of the lua functions to define a function in event_mapgen.lua that will change wall tiles near the middle of the map into water tiles to simulate a river:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Halcyon custom map gen steps&lt;br /&gt;
--used for making the river in the Illuminant Riverbed&lt;br /&gt;
function FLOOR_GEN_SCRIPT.CreateRiver(map, args)&lt;br /&gt;
	local mapCenter = math.ceil(map.Width / 2)&lt;br /&gt;
	local randomOffset = map.Rand:Next(-2,3) --a random small offset added to all tiles to help randomize where the river falls a bit &lt;br /&gt;
	local leftBound = math.floor(mapCenter / 2) + randomOffset --base left bound &lt;br /&gt;
	local rightBound = math.ceil(mapCenter * 3 / 2) + randomOffset -- base right bound&lt;br /&gt;
	local leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local leftShore = 0&lt;br /&gt;
	local rightShore = 0&lt;br /&gt;
	&lt;br /&gt;
	local leftOffsetRemaining = map.Rand:Next(1, 5)--how many times this specific offset can be used before being regenerated &lt;br /&gt;
	local rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	--go row by row. Replace ground tiles towards the center of the map with water tiles to create a river flowing through the dungeon.&lt;br /&gt;
	--Ground tiles will remain untouched. River will ebb a bit side to side within a limit.&lt;br /&gt;
	&lt;br /&gt;
	for y = 0, map.Height-1, 1 do &lt;br /&gt;
		&lt;br /&gt;
		--determine starting and ending positions for row of river&lt;br /&gt;
		--an offset will last for a few rows before trying to roll again for a new offset&lt;br /&gt;
		&lt;br /&gt;
		--roll new offsets and set new offset timer &lt;br /&gt;
		--NOTE: map.Rand:Next(lower, upper) is inclusive on lower, and exclusive on upper &lt;br /&gt;
		if leftOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if leftOffset &amp;lt; 0 then&lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif leftOffset &amp;gt; 0 then &lt;br /&gt;
				leftOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			leftOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if rightOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if rightOffset &amp;lt; 0 then&lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif rightOffset &amp;gt; 0 then &lt;br /&gt;
				rightOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		leftShore = leftBound + leftOffset&lt;br /&gt;
		rightShore = rightBound + rightOffset&lt;br /&gt;
				&lt;br /&gt;
		--set all non ground tiles to water tiles between our left and right bounds &lt;br /&gt;
		for x = leftShore, rightShore, 1 do &lt;br /&gt;
			local loc = RogueElements.Loc(x, y)&lt;br /&gt;
			if not map:GetTile(loc):TileEquivalent(map.RoomTerrain) then&lt;br /&gt;
				map:TrySetTile(loc, RogueEssence.Dungeon.Tile(&amp;quot;water&amp;quot;))&lt;br /&gt;
			end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		leftOffsetRemaining = leftOffsetRemaining - 1&lt;br /&gt;
		rightOffsetRemaining = rightOffsetRemaining - 1&lt;br /&gt;
		&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Now that we have our function defined, we need to actually add it to the dungeon floor! This is done by adding it to the gen steps for the floor in the dungeon setup. Where you place it in orderwise depends on what your genstep does, but in our case, it&#039;s something that should be ran after all other terrain generation has finished, so we&#039;ll put it at priority 4 here. For the Script area, we write the name of our script (in this case, CreateRiver), and define any arguments we may have (in this case, none).&lt;br /&gt;
&lt;br /&gt;
[[File:Custom Gen Step Instructions.PNG|frameless]]&lt;br /&gt;
&lt;br /&gt;
Then, we just load up our floor, and voila! We have a simple but decent looking river now flowing through our dungeon floor!&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1372</id>
		<title>Tutorial:Custom Floor Generation Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1372"/>
		<updated>2023-07-06T18:09:40Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&#039;ll walk through an example on how this was done for Halcyon&#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on what exactly you&#039;re trying to achieve with your custom gen step, you may want to start with the vanilla gen steps to get a base to work off of, or you may want to start with the Lua customization first. Either way, once you&#039;re ready to sit down and work with the Lua scripting, there&#039;s some basic functions/knowledge you&#039;ll need to know. There are more functions beyond these, but these are all we need for creating a river. You may find additional functions and tools to do things such as creating traps, items, or NPCs in the FLOOR_GEN_SCRIPT.Test function in event_mapgen.lua.&lt;br /&gt;
&lt;br /&gt;
The function that defines your custom gen function should be given two parameters: map and args.&lt;br /&gt;
Map is the floor you&#039;re on that you&#039;re generating. It includes a number of useful functions and variables such as:&lt;br /&gt;
  * map.Width: Width of the map&lt;br /&gt;
  * map.Height: Height of the map&lt;br /&gt;
  * map.Rand:Next(x, y) - returns a random number, inclusive on lower and exclusive on upper, between x and y. This is important to use instead of math.random so that the dungeon&#039;s random seeding is used to generate the random number so replays and such are consistent.&lt;br /&gt;
  * map:GetTile(loc) - gets the tile at the specified RogueElements.Loc(x,y).&lt;br /&gt;
  * map:TrySetTile(loc, terrain) - attempts to set the tile at the specified location to the specified terrain type. May fail if the tile should not be changed for some reason (for example, it is the loc of the stairs)&lt;br /&gt;
    &lt;br /&gt;
Args is a list of arguments given in the Arg Table in the ScriptGenStep. Using these, you can write somewhat generic generation algorithms that can be tweaked on a per floor basis by giving different arguments to the function via the genstep&#039;s arg table!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, we need to figure out how we&#039;d want to go about defining what constitutes our river. In this case, we want the following attributes for our river:&lt;br /&gt;
  * It takes up about half the map, with its center being around the middle of the dungeon&#039;s X axis.&lt;br /&gt;
  * It flows vertically.&lt;br /&gt;
  * Its border randomly shifts left and right a few tiles every so often, but not so far as to shift too far from the center.&lt;br /&gt;
  * Its border never shifts 2 water tiles at a time, or else it may look jagged and strange.&lt;br /&gt;
  * It doesn&#039;t override ground tiles, only wall tiles. This way rooms and hallways are not changed into water.&lt;br /&gt;
&lt;br /&gt;
And with that, we use this and our knowledge of the lua functions to define a function in event_mapgen.lua that will change wall tiles near the middle of the map into water tiles to simulate a river:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Halcyon custom map gen steps&lt;br /&gt;
--used for making the river in the Illuminant Riverbed&lt;br /&gt;
function FLOOR_GEN_SCRIPT.CreateRiver(map, args)&lt;br /&gt;
	local mapCenter = math.ceil(map.Width / 2)&lt;br /&gt;
	local randomOffset = map.Rand:Next(-2,3) --a random small offset added to all tiles to help randomize where the river falls a bit &lt;br /&gt;
	local leftBound = math.floor(mapCenter / 2) + randomOffset --base left bound &lt;br /&gt;
	local rightBound = math.ceil(mapCenter * 3 / 2) + randomOffset -- base right bound&lt;br /&gt;
	local leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local leftShore = 0&lt;br /&gt;
	local rightShore = 0&lt;br /&gt;
	&lt;br /&gt;
	local leftOffsetRemaining = map.Rand:Next(1, 5)--how many times this specific offset can be used before being regenerated &lt;br /&gt;
	local rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	--go row by row. Replace ground tiles towards the center of the map with water tiles to create a river flowing through the dungeon.&lt;br /&gt;
	--Ground tiles will remain untouched. River will ebb a bit side to side within a limit.&lt;br /&gt;
	&lt;br /&gt;
	for y = 0, map.Height-1, 1 do &lt;br /&gt;
		&lt;br /&gt;
		--determine starting and ending positions for row of river&lt;br /&gt;
		--an offset will last for a few rows before trying to roll again for a new offset&lt;br /&gt;
		&lt;br /&gt;
		--roll new offsets and set new offset timer &lt;br /&gt;
		--NOTE: map.Rand:Next(lower, upper) is inclusive on lower, and exclusive on upper &lt;br /&gt;
		if leftOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if leftOffset &amp;lt; 0 then&lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif leftOffset &amp;gt; 0 then &lt;br /&gt;
				leftOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			leftOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if rightOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if rightOffset &amp;lt; 0 then&lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif rightOffset &amp;gt; 0 then &lt;br /&gt;
				rightOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		leftShore = leftBound + leftOffset&lt;br /&gt;
		rightShore = rightBound + rightOffset&lt;br /&gt;
				&lt;br /&gt;
		--set all non ground tiles to water tiles between our left and right bounds &lt;br /&gt;
		for x = leftShore, rightShore, 1 do &lt;br /&gt;
			local loc = RogueElements.Loc(x, y)&lt;br /&gt;
			if not map:GetTile(loc):TileEquivalent(map.RoomTerrain) then&lt;br /&gt;
				map:TrySetTile(loc, RogueEssence.Dungeon.Tile(&amp;quot;water&amp;quot;))&lt;br /&gt;
			end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		leftOffsetRemaining = leftOffsetRemaining - 1&lt;br /&gt;
		rightOffsetRemaining = rightOffsetRemaining - 1&lt;br /&gt;
		&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Now that we have our function defined, we need to actually add it to the dungeon floor! This is done by adding it to the gen steps for the floor in the dungeon setup. Where you place it in orderwise depends on what your genstep does, but in our case, it&#039;s something that should be ran after all other terrain generation has finished, so we&#039;ll put it at priority 4 here. For the Script area, we write the name of our script (in this case, CreateRiver), and define any arguments we may have (in this case, none).&lt;br /&gt;
&lt;br /&gt;
[[File:Custom Gen Step Instructions.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
Then, we just load up our floor, and voila! We have a simple but decent looking river now flowing through our dungeon floor!&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Custom_Gen_Step_Instructions.PNG&amp;diff=1371</id>
		<title>File:Custom Gen Step Instructions.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:Custom_Gen_Step_Instructions.PNG&amp;diff=1371"/>
		<updated>2023-07-06T18:09:04Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:FOWyQRU-1-.png&amp;diff=1370</id>
		<title>File:FOWyQRU-1-.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:FOWyQRU-1-.png&amp;diff=1370"/>
		<updated>2023-07-06T18:07:24Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:2sBVFyb-1-.png&amp;diff=1369</id>
		<title>File:2sBVFyb-1-.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=File:2sBVFyb-1-.png&amp;diff=1369"/>
		<updated>2023-07-06T18:07:16Z</updated>

		<summary type="html">&lt;p&gt;Palika: Uploaded with SimpleBatchUpload&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1368</id>
		<title>Tutorial:Custom Floor Generation Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1368"/>
		<updated>2023-07-06T18:01:49Z</updated>

		<summary type="html">&lt;p&gt;Palika: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&#039;ll walk through an example on how this was done for Halcyon&#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on what exactly you&#039;re trying to achieve with your custom gen step, you may want to start with the vanilla gen steps to get a base to work off of, or you may want to start with the Lua customization first. Either way, once you&#039;re ready to sit down and work with the Lua scripting, there&#039;s some basic functions/knowledge you&#039;ll need to know. There are more functions beyond these, but these are all we need for creating a river. You may find additional functions and tools to do things such as creating traps, items, or NPCs in the FLOOR_GEN_SCRIPT.Test function in event_mapgen.lua.&lt;br /&gt;
&lt;br /&gt;
The function that defines your custom gen function should be given two parameters: map and args.&lt;br /&gt;
Map is the floor you&#039;re on that you&#039;re generating. It includes a number of useful functions and variables such as:&lt;br /&gt;
  * map.Width: Width of the map&lt;br /&gt;
  * map.Height: Height of the map&lt;br /&gt;
  * map.Rand:Next(x, y) - returns a random number, inclusive on lower and exclusive on upper, between x and y. This is important to use instead of math.random so that the dungeon&#039;s random seeding is used to generate the random number so replays and such are consistent.&lt;br /&gt;
  * map:GetTile(loc) - gets the tile at the specified RogueElements.Loc(x,y).&lt;br /&gt;
  * map:TrySetTile(loc, terrain) - attempts to set the tile at the specified location to the specified terrain type. May fail if the tile should not be changed for some reason (for example, it is the loc of the stairs)&lt;br /&gt;
    &lt;br /&gt;
Args is a list of arguments given in the Arg Table in the ScriptGenStep. Using these, you can write somewhat generic generation algorithms that can be tweaked on a per floor basis by giving different arguments to the function via the genstep&#039;s arg table!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, we need to figure out how we&#039;d want to go about defining what constitutes our river. In this case, we want the following attributes for our river:&lt;br /&gt;
  * It takes up about half the map, with its center being around the middle of the dungeon&#039;s X axis.&lt;br /&gt;
  * It flows vertically.&lt;br /&gt;
  * Its border randomly shifts left and right a few tiles every so often, but not so far as to shift too far from the center.&lt;br /&gt;
  * Its border never shifts 2 water tiles at a time, or else it may look jagged and strange.&lt;br /&gt;
  * It doesn&#039;t override ground tiles, only wall tiles. This way rooms and hallways are not changed into water.&lt;br /&gt;
&lt;br /&gt;
And with that, we use this and our knowledge of the lua functions to define a function in event_mapgen.lua that will change wall tiles near the middle of the map into water tiles to simulate a river:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Halcyon custom map gen steps&lt;br /&gt;
--used for making the river in the Illuminant Riverbed&lt;br /&gt;
function FLOOR_GEN_SCRIPT.CreateRiver(map, args)&lt;br /&gt;
	local mapCenter = math.ceil(map.Width / 2)&lt;br /&gt;
	local randomOffset = map.Rand:Next(-2,3) --a random small offset added to all tiles to help randomize where the river falls a bit &lt;br /&gt;
	local leftBound = math.floor(mapCenter / 2) + randomOffset --base left bound &lt;br /&gt;
	local rightBound = math.ceil(mapCenter * 3 / 2) + randomOffset -- base right bound&lt;br /&gt;
	local leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local leftShore = 0&lt;br /&gt;
	local rightShore = 0&lt;br /&gt;
	&lt;br /&gt;
	local leftOffsetRemaining = map.Rand:Next(1, 5)--how many times this specific offset can be used before being regenerated &lt;br /&gt;
	local rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	--go row by row. Replace ground tiles towards the center of the map with water tiles to create a river flowing through the dungeon.&lt;br /&gt;
	--Ground tiles will remain untouched. River will ebb a bit side to side within a limit.&lt;br /&gt;
	&lt;br /&gt;
	for y = 0, map.Height-1, 1 do &lt;br /&gt;
		&lt;br /&gt;
		--determine starting and ending positions for row of river&lt;br /&gt;
		--an offset will last for a few rows before trying to roll again for a new offset&lt;br /&gt;
		&lt;br /&gt;
		--roll new offsets and set new offset timer &lt;br /&gt;
		--NOTE: map.Rand:Next(lower, upper) is inclusive on lower, and exclusive on upper &lt;br /&gt;
		if leftOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if leftOffset &amp;lt; 0 then&lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif leftOffset &amp;gt; 0 then &lt;br /&gt;
				leftOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			leftOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if rightOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if rightOffset &amp;lt; 0 then&lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif rightOffset &amp;gt; 0 then &lt;br /&gt;
				rightOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		leftShore = leftBound + leftOffset&lt;br /&gt;
		rightShore = rightBound + rightOffset&lt;br /&gt;
				&lt;br /&gt;
		--set all non ground tiles to water tiles between our left and right bounds &lt;br /&gt;
		for x = leftShore, rightShore, 1 do &lt;br /&gt;
			local loc = RogueElements.Loc(x, y)&lt;br /&gt;
			if not map:GetTile(loc):TileEquivalent(map.RoomTerrain) then&lt;br /&gt;
				map:TrySetTile(loc, RogueEssence.Dungeon.Tile(&amp;quot;water&amp;quot;))&lt;br /&gt;
			end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		leftOffsetRemaining = leftOffsetRemaining - 1&lt;br /&gt;
		rightOffsetRemaining = rightOffsetRemaining - 1&lt;br /&gt;
		&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Now that we have our function defined, we need to actually add it to the dungeon floor! This is done by adding it to the gen steps for the floor in the dungeon setup. Where you place it in orderwise depends on what your genstep does, but in our case, it&#039;s something that should be ran after all other terrain generation has finished, so we&#039;ll put it at priority 4 here. For the Script area, we write the name of our script (in this case, CreateRiver), and define any arguments we may have (in this case, none).&lt;br /&gt;
&lt;br /&gt;
[EDITOR PICTURE HERE]&lt;br /&gt;
&lt;br /&gt;
Then, we just load up our floor, and voila! We have a simple but decent looking river now flowing through our dungeon floor!&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
	<entry>
		<id>https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1367</id>
		<title>Tutorial:Custom Floor Generation Steps</title>
		<link rel="alternate" type="text/html" href="https://wiki.pmdo.pmdcollab.org/wiki/index.php?title=Tutorial:Custom_Floor_Generation_Steps&amp;diff=1367"/>
		<updated>2023-07-06T18:01:10Z</updated>

		<summary type="html">&lt;p&gt;Palika: Created page with &amp;quot;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&amp;#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&amp;#039;ll walk through an example on how this was done for Halcyon&amp;#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do thi...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Sometimes, you may find that you want to add a feature to dungeon generation that just isn&#039;t possible with built in gen steps and tools. When this happens, you can script the generation step or feature in yourself by scripting via Lua by creating a FLOOR_GEN_SCRIPT in event_mapgen.lua. We&#039;ll walk through an example on how this was done for Halcyon&#039;s Illuminant Riverbed to show an example of how you can do this and some of the useful Lua functions you might need to do this.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Depending on what exactly you&#039;re trying to achieve with your custom gen step, you may want to start with the vanilla gen steps to get a base to work off of, or you may want to start with the Lua customization first. Either way, once you&#039;re ready to sit down and work with the Lua scripting, there&#039;s some basic functions/knowledge you&#039;ll need to know. There are more functions beyond these, but these are all we need for creating a river. You may find additional functions and tools to do things such as creating traps, items, or NPCs in the FLOOR_GEN_SCRIPT.Test function in event_mapgen.lua.&lt;br /&gt;
&lt;br /&gt;
* The function that defines your custom gen function should be given two parameters: map and args.&lt;br /&gt;
  * map is the floor you&#039;re on that you&#039;re generating. It includes a number of useful functions and variables such as:&lt;br /&gt;
    * map.Width: Width of the map&lt;br /&gt;
    * map.Height: Height of the map&lt;br /&gt;
    * map.Rand:Next(x, y) - returns a random number, inclusive on lower and exclusive on upper, between x and y. This is important to use instead of math.random so that the dungeon&#039;s random seeding is used to generate the random number so replays and such are consistent.&lt;br /&gt;
    * map:GetTile(loc) - gets the tile at the specified RogueElements.Loc(x,y).&lt;br /&gt;
    * map:TrySetTile(loc, terrain) - attempts to set the tile at the specified location to the specified terrain type. May fail if the tile should not be changed for some reason (for example, it is the loc of the stairs)&lt;br /&gt;
    &lt;br /&gt;
  * args is a list of arguments given in the Arg Table in the ScriptGenStep. Using these, you can write somewhat generic generation algorithms that can be tweaked on a per floor basis by giving different arguments to the function via the genstep&#039;s arg table!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, we need to figure out how we&#039;d want to go about defining what constitutes our river. In this case, we want the following attributes for our river:&lt;br /&gt;
  * It takes up about half the map, with its center being around the middle of the dungeon&#039;s X axis.&lt;br /&gt;
  * It flows vertically.&lt;br /&gt;
  * Its border randomly shifts left and right a few tiles every so often, but not so far as to shift too far from the center.&lt;br /&gt;
  * Its border never shifts 2 water tiles at a time, or else it may look jagged and strange.&lt;br /&gt;
  * It doesn&#039;t override ground tiles, only wall tiles. This way rooms and hallways are not changed into water.&lt;br /&gt;
&lt;br /&gt;
And with that, we use this and our knowledge of the lua functions to define a function in event_mapgen.lua that will change wall tiles near the middle of the map into water tiles to simulate a river:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--Halcyon custom map gen steps&lt;br /&gt;
--used for making the river in the Illuminant Riverbed&lt;br /&gt;
function FLOOR_GEN_SCRIPT.CreateRiver(map, args)&lt;br /&gt;
	local mapCenter = math.ceil(map.Width / 2)&lt;br /&gt;
	local randomOffset = map.Rand:Next(-2,3) --a random small offset added to all tiles to help randomize where the river falls a bit &lt;br /&gt;
	local leftBound = math.floor(mapCenter / 2) + randomOffset --base left bound &lt;br /&gt;
	local rightBound = math.ceil(mapCenter * 3 / 2) + randomOffset -- base right bound&lt;br /&gt;
	local leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
	local leftShore = 0&lt;br /&gt;
	local rightShore = 0&lt;br /&gt;
	&lt;br /&gt;
	local leftOffsetRemaining = map.Rand:Next(1, 5)--how many times this specific offset can be used before being regenerated &lt;br /&gt;
	local rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
	--go row by row. Replace ground tiles towards the center of the map with water tiles to create a river flowing through the dungeon.&lt;br /&gt;
	--Ground tiles will remain untouched. River will ebb a bit side to side within a limit.&lt;br /&gt;
	&lt;br /&gt;
	for y = 0, map.Height-1, 1 do &lt;br /&gt;
		&lt;br /&gt;
		--determine starting and ending positions for row of river&lt;br /&gt;
		--an offset will last for a few rows before trying to roll again for a new offset&lt;br /&gt;
		&lt;br /&gt;
		--roll new offsets and set new offset timer &lt;br /&gt;
		--NOTE: map.Rand:Next(lower, upper) is inclusive on lower, and exclusive on upper &lt;br /&gt;
		if leftOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if leftOffset &amp;lt; 0 then&lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif leftOffset &amp;gt; 0 then &lt;br /&gt;
				leftOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				leftOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			leftOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if rightOffsetRemaining &amp;lt;= 0 then&lt;br /&gt;
			if rightOffset &amp;lt; 0 then&lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 1)&lt;br /&gt;
			elseif rightOffset &amp;gt; 0 then &lt;br /&gt;
				rightOffset = map.Rand:Next(0, 2)&lt;br /&gt;
			else &lt;br /&gt;
				rightOffset = map.Rand:Next(-1, 2)&lt;br /&gt;
			end&lt;br /&gt;
			rightOffsetRemaining = map.Rand:Next(1, 5)&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		leftShore = leftBound + leftOffset&lt;br /&gt;
		rightShore = rightBound + rightOffset&lt;br /&gt;
				&lt;br /&gt;
		--set all non ground tiles to water tiles between our left and right bounds &lt;br /&gt;
		for x = leftShore, rightShore, 1 do &lt;br /&gt;
			local loc = RogueElements.Loc(x, y)&lt;br /&gt;
			if not map:GetTile(loc):TileEquivalent(map.RoomTerrain) then&lt;br /&gt;
				map:TrySetTile(loc, RogueEssence.Dungeon.Tile(&amp;quot;water&amp;quot;))&lt;br /&gt;
			end&lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
		end &lt;br /&gt;
		&lt;br /&gt;
		leftOffsetRemaining = leftOffsetRemaining - 1&lt;br /&gt;
		rightOffsetRemaining = rightOffsetRemaining - 1&lt;br /&gt;
		&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
Now that we have our function defined, we need to actually add it to the dungeon floor! This is done by adding it to the gen steps for the floor in the dungeon setup. Where you place it in orderwise depends on what your genstep does, but in our case, it&#039;s something that should be ran after all other terrain generation has finished, so we&#039;ll put it at priority 4 here. For the Script area, we write the name of our script (in this case, CreateRiver), and define any arguments we may have (in this case, none).&lt;br /&gt;
&lt;br /&gt;
[EDITOR PICTURE HERE]&lt;br /&gt;
&lt;br /&gt;
Then, we just load up our floor, and voila! We have a simple but decent looking river now flowing through our dungeon floor!&lt;/div&gt;</summary>
		<author><name>Palika</name></author>
	</entry>
</feed>