SingleEvent Scripts: Difference between revisions
No edit summary |
format and editing |
||
Line 1: | Line 1: | ||
These events go into the <code>event_single.lua</code> 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. | |||
== Custom Boss Fight Win Conditions / End effects: == | |||
:'''Author:''' Palika | |||
Want to have it so a boss fight has an alternative win condition than just "beat all enemies"? Want something special to happen when that win condition is met? If the answer is yes, you'll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the Lua version. | |||
=== Code === | |||
<pre> | |||
--- | |||
-- Reimplementation of Audino's C# event for BeginBattleEvent. | |||
-- This will allow us to inject custom function code before ending a battle | |||
-- (such as clearing existing lava flows when a boss fight ends). | |||
-- @tparam GameEventOwner owner | |||
-- @tparam Character ownerChar | |||
-- @tparam SingleCharContext context | |||
-- @tparam table args Table of format {CustomClearEvent="NameOfFunctionThatRunsOnClear", [other arguments to said script]} | |||
function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args) | function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args) | ||
local MapCheckState = luanet.import_type('RogueEssence.Dungeon.MapCheckState') | |||
local SingleCharScriptEvent = luanet.import_type('RogueEssence.Dungeon.SingleCharScriptEvent') | |||
local map_clear_idx = 'map_clear_check' | local map_clear_idx = 'map_clear_check' | ||
if context.User ~= nil then return end | if context.User ~= nil then return end | ||
Line 38: | Line 44: | ||
end | end | ||
--Reimplementation of the basic CheckBossClearEvent. | --- | ||
--Call something different from LuaBeginBattleEvent or edit this accordingly if you want a different wincon for your map or special effects/anims on win. | -- Reimplementation of the basic CheckBossClearEvent. | ||
-- Call something different from LuaBeginBattleEvent or edit this accordingly if you | |||
-- want a different wincon for your map or special effects/anims on win. | |||
-- @tparam GameEventOwner owner | |||
-- @tparam Character ownerChar Not used by this function. | |||
-- @tparam SingleCharContext context Not used by this function. | |||
-- @tparam table args Table of caller-defined arguments. Not used by this function. | |||
function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args) | function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args) | ||
local MapCheckState = luanet.import_type('RogueEssence.Dungeon.MapCheckState') | |||
local SingleCharScriptEvent = luanet.import_type('RogueEssence.Dungeon.SingleCharScriptEvent') | |||
--Sequence that runs when map is over. Fade out, cut the music, etc. | --- | ||
-- Sequence that runs when map is over. Fade out, cut the music, etc. | |||
-- @usage Coroutine | |||
function end_sequence() | function end_sequence() | ||
_GAME:BGM("", true) | _GAME:BGM("", true) | ||
Line 50: | Line 66: | ||
_DUNGEON:ResetTurns() | _DUNGEON:ResetTurns() | ||
--restore all and remove all map status | -- restore all and remove all map status | ||
local statuses_to_remove = {} | local statuses_to_remove = {} | ||
for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do | for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do | ||
Line 60: | Line 76: | ||
end | end | ||
--heal everyone in the party | -- heal everyone in the party | ||
for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do | for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do | ||
_DATA.Save.ActiveTeam.Players[i]:FullRestore() | _DATA.Save.ActiveTeam.Players[i]:FullRestore() | ||
Line 69: | Line 85: | ||
end | end | ||
--For each enemy team, check each chara in that team. If any are still alive, then fail this check and return early. | -- For each enemy team, check each chara in that team. | ||
-- If any are still alive, then fail this check and return early. | |||
for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do | for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do | ||
local team = _ZONE.CurrentMap.MapTeams[i].Players | local team = _ZONE.CurrentMap.MapTeams[i].Players | ||
for j = 0, team.Count - 1, 1 do | for j = 0, team.Count - 1, 1 do | ||
--Break and return early if even one enemy is not dead. | -- Break and return early if even one enemy is not dead. | ||
if not team[j].Dead then return end | if not team[j].Dead then return end | ||
end | end | ||
end | end | ||
--Everyone's dead, clear the scene. | -- Everyone's dead, clear the scene. | ||
local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState)) | local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState)) | ||
--The call originally for this was to remove(this), which isn't in lua. So we need to find the LuaCheckBossClearEvent and remove that (remove ourself) | -- The call originally for this was to remove(this), which isn't in lua. | ||
-- So we need to find the LuaCheckBossClearEvent and remove that (remove ourself) | |||
for i = 0, checks.CheckEvents.Count - 1, 1 do | for i = 0, checks.CheckEvents.Count - 1, 1 do | ||
if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then | if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then | ||
Line 97: | Line 115: | ||
end | end | ||
</pre> | |||
=== Usage === | |||
The first function, <code>LuaBeginBattleEvent</code>, is responsible for letting PMDO know to look at <code>LuaCheckBossClearEvent</code> to determine when the map is won. Instead of the usual <code>BeginBattleEvent</code> in your boss fight's OnMapStart's Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It'll look like this: | |||
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's OnMapStart's Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It'll look like this: | |||
[[File:LuaBeginBattleEvent.PNG]] | [[File:LuaBeginBattleEvent.PNG]] | ||
<code>LuaCheckBossClearEvent</code> is responsible for identifying when the map is won, and then the <code>end_sequence</code> function within it is responsible for fading stuff out and actually winning the map. | |||
You won't need to modify the scripting for LuaBeginBattleEvent at all; it should remain as it is. | |||
==== Tutorial: Alternate win condition ==== | |||
If you would like an alternate win condition or end sequence besides just fading out, then you will need to edit <code>LuaCheckBossClearEvent</code> and its <code>end_sequence</code> respectively. | |||
# First, copy and rename your custom version of <code>LuaCheckBossClearEvent</code> to something more appropriately fitting. | |||
#* Let's say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We'll call this function instead <code>CheckDefeatedGangLeader</code>. Copy the <code>LuaCheckBossClearEvent</code> function above and rename it to <code>CheckDefeatedGangLeader</code>. | |||
# In <code>LuaBeginBattleEvent</code>, be sure to pass an argument of <code>CustomClearEvent = 'CheckDefeatedGangLeader'</code> when you define it in the MapEffects, so <code>LuaBeginBattleEvent</code> will knows to use your custom function instead of the default one given. | |||
# Next, you need to modify how <code>CheckDefeatedGangLeader</code> 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. | |||
#* 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. | |||
#* Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say, <code>'GangLeader' = true</code>, and make sure that there are no NPCs with a LuaTable property of 'GangLeader' 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. | |||
# With all this done, when the Scrafty is defeated, the map should end and call <code>end_sequence()</code> as long as we aren't watching a replay. <code>end_sequence()</code> 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 may be important to handling the end of the dungeon adventure, such as the line of code responsible for ending the segment as Cleared. |
Revision as of 20:35, 13 March 2024
These events go into the event_single.lua
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.
Custom Boss Fight Win Conditions / End effects:
- Author: Palika
Want to have it so a boss fight has an alternative win condition than just "beat all enemies"? Want something special to happen when that win condition is met? If the answer is yes, you'll need to override the usual BeginBattleEvent and its corresponding CheckBossClearEvent and end_sequence responsible for these features with the Lua version.
Code
--- -- Reimplementation of Audino's C# event for BeginBattleEvent. -- This will allow us to inject custom function code before ending a battle -- (such as clearing existing lava flows when a boss fight ends). -- @tparam GameEventOwner owner -- @tparam Character ownerChar -- @tparam SingleCharContext context -- @tparam table args Table of format {CustomClearEvent="NameOfFunctionThatRunsOnClear", [other arguments to said script]} function SINGLE_CHAR_SCRIPT.LuaBeginBattleEvent(owner, ownerChar, context, args) local MapCheckState = luanet.import_type('RogueEssence.Dungeon.MapCheckState') local SingleCharScriptEvent = luanet.import_type('RogueEssence.Dungeon.SingleCharScriptEvent') local map_clear_idx = 'map_clear_check' if context.User ~= nil then return end --if a custom clear event is not given, use the default one. if args.CustomClearEvent == nil then args.CustomClearEvent = 'LuaCheckBossClearEvent' end --Turn on Team Mode if allowed when the boss fight starts. if _DUNGEON:CanUseTeamMode() then _DUNGEON:SetTeamMode(true) end local clear_status = RogueEssence.Dungeon.MapStatus(map_clear_idx) clear_status:LoadFromData() local check = clear_status.StatusStates:GetWithDefault(luanet.ctype(MapCheckState)) --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. --We only NEED to pass args, as owner, ownerchar, and context are automatically passed in when the check event is called check.CheckEvents:Add(SingleCharScriptEvent(args.CustomClearEvent, Serpent.line(args))) --check.CheckEvents:Add(LuaCheckBossClearEvent(owner, ownerChar, context, args)) TASK:WaitTask(_DUNGEON:AddMapStatus(clear_status)) end --- -- Reimplementation of the basic CheckBossClearEvent. -- Call something different from LuaBeginBattleEvent or edit this accordingly if you -- want a different wincon for your map or special effects/anims on win. -- @tparam GameEventOwner owner -- @tparam Character ownerChar Not used by this function. -- @tparam SingleCharContext context Not used by this function. -- @tparam table args Table of caller-defined arguments. Not used by this function. function SINGLE_CHAR_SCRIPT.LuaCheckBossClearEvent(owner, ownerChar, context, args) local MapCheckState = luanet.import_type('RogueEssence.Dungeon.MapCheckState') local SingleCharScriptEvent = luanet.import_type('RogueEssence.Dungeon.SingleCharScriptEvent') --- -- Sequence that runs when map is over. Fade out, cut the music, etc. -- @usage Coroutine function end_sequence() _GAME:BGM("", true) TASK:WaitTask(_GAME:FadeOut(false)) _DUNGEON:ResetTurns() -- restore all and remove all map status local statuses_to_remove = {} for i = 0, _ZONE.CurrentMap.Status.Keys.Count - 1, 1 do statuses_to_remove[i] = _ZONE.CurrentMap.Status.Keys[i] end for i = 0, #statuses_to_remove - 1, 1 do TASK:WaitTask(_DUNGEON:RemoveMapStatus(statuses_to_remove[i], false)) end -- heal everyone in the party for i = 0, GAME:GetPlayerPartyCount() - 1, 1 do _DATA.Save.ActiveTeam.Players[i]:FullRestore() end TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared)) end -- For each enemy team, check each chara in that team. -- If any are still alive, then fail this check and return early. for i = 0, _ZONE.CurrentMap.MapTeams.Count - 1, 1 do local team = _ZONE.CurrentMap.MapTeams[i].Players for j = 0, team.Count - 1, 1 do -- Break and return early if even one enemy is not dead. if not team[j].Dead then return end end end -- Everyone's dead, clear the scene. local checks = owner.StatusStates:GetWithDefault(luanet.ctype(MapCheckState)) -- The call originally for this was to remove(this), which isn't in lua. -- So we need to find the LuaCheckBossClearEvent and remove that (remove ourself) for i = 0, checks.CheckEvents.Count - 1, 1 do if LUA_ENGINE:TypeOf(checks.CheckEvents[i]) == luanet.ctype(SingleCharScriptEvent) then if checks.CheckEvents[i].Script == args.CustomClearEvent then checks.CheckEvents:Remove(checks.CheckEvents[i]) end end end if _DATA.CurrentReplay == nil then TASK:WaitTask(end_sequence()) else TASK:WaitTask(_GAME:EndSegment(RogueEssence.Data.GameProgress.ResultType.Cleared)) end end
Usage
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's OnMapStart's Map Effects, you need to call a SingleCharScriptEvent for LuaBeginBattleEvent instead. It'll look like this:
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.
You won't need to modify the scripting for LuaBeginBattleEvent at all; it should remain as it is.
Tutorial: Alternate win condition
If you would like an alternate win condition or end sequence besides just fading out, then you will need to edit LuaCheckBossClearEvent
and its end_sequence
respectively.
- First, copy and rename your custom version of
LuaCheckBossClearEvent
to something more appropriately fitting.- Let's say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We'll call this function instead
CheckDefeatedGangLeader
. Copy theLuaCheckBossClearEvent
function above and rename it toCheckDefeatedGangLeader
.
- Let's say we want a win condition where you only have to defeat the leader of a gang to win the boss fight. We'll call this function instead
- In
LuaBeginBattleEvent
, be sure to pass an argument ofCustomClearEvent = 'CheckDefeatedGangLeader'
when you define it in the MapEffects, soLuaBeginBattleEvent
will knows to use your custom function instead of the default one given. - Next, you need to modify how
CheckDefeatedGangLeader
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.- 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.
- Alternatively, you can assign a LuaTable property to the Scrafty that marks it as, say,
'GangLeader' = true
, and make sure that there are no NPCs with a LuaTable property of 'GangLeader' 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.
- With all this done, when the Scrafty is defeated, the map should end and call
end_sequence()
as long as we aren'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 may be important to handling the end of the dungeon adventure, such as the line of code responsible for ending the segment as Cleared.