User File #638615107657589700

Upload All User Files

#638615107657589700 - Mario Party 8 RNG manipulation helper v1.0

MP8_RNG_manipulation_helper.lua
Game: Mario Party 8 ( Wii, see all files )
28 downloads
Uploaded 9/9/2024 8:32 PM by mohoc (see all 26)
My first Lua script. Enjoy!
-------------------------------------------
-- MARIO PARTY 8 RNG MANIPULATION HELPER --
-------------------------------------------
-- Author: mohoc.
-- Created on 27/08/2024.
-- Last modified on 09/09/2024.
-- You are free to use and modify this code. Enjoy!
---------------------------------------------------
-- Requirements:
-- 1. Use Lua Core v4.3 by MikeXander
---- https://github.com/MikeXander/Dolphin-Lua-Core/releases/tag/v4.3
-- 2. No GameCube controllers and a single Wiimote.
---------------------------------------------------

------------------
-- INSTRUCTIONS --
------------------
-- Getting started
-- 1. Put this file in the "Sys > Scripts" folder of the emulator.
-- 2. On the emulator: go to "Tools > Execute Script" and select this script.

-- Preparation
-- 1. Use the main global variables below to define your sequence of actions
-- and the range of authorized delay values for each action.
---- See the "ACTIONS MACRO" section for a list of already defined actions.
-- 2. Make a save state a couple frames before when the first action must happen.

-- Running the script
-- 1. Press "Start" in the "Lauch Script" window ("Tools > Execute Script").
-- 2. Load your save state.
-- 2.b. (Optional:) frame advance once you load your save state.
---- This will display the results of the sanity checks.
---- If "Getting started." is displayed then it should be good.
---- Otherwise you will get a warning.
-- 3. Unpause the emulator to play the first sequence.
---- You may pause, make save states or enter extra inputs manually at will.
-- 4. Reload your save state to play the next sequence.
---- Sequences are considered in lexicographic order of their delay sequences.
---- You must go past the first action. Otherwise, the sequence will not be updated.
-- 5. Once the last sequence is played, loading your save state will end the script.

---------------------------
-- MAIN GLOBAL VARIABLES --
---------------------------
-- To be modified.
-- Note: consistently selecting menu options requires you to 
-- point at it for at least 3 frames and then press A.

seqActions = {"A"}
seqFrames = {90}
seqDelayUpperBounds = {10}
seqDelayLowerBounds = {0}

maxTotalDelay = 10
minTotalDelay = 0


--[[
-- Example 1: DK board, pre-turn 1
-- seqActions = {"+", "A", "A", "pNoLow", "pNoLow", "pNoLow", "A", "A", "shake", "shake"}
-- seqFrames = {5332, 5394, 5405, 5442, 5443, 5444, 5445, 5486, 5527, 5528}
-- seqDelayUpperBounds = {3, 1, 1, 1, 0, 0, 0, 3, 0, 0}
-- seqDelayLowerBounds = {3, 0, 0, 0, 0, 0, 0, 3, 0, 0}
--]]

--[[
-- Example 2: Shy Guy board, turn 1
seqActions = {"A", "shake",
	"pNoLow", "pNoLow", "pNoLow", "A", 
	"A", "shake"}
seqFrames = {25476, 25519,
	25661, 25662, 25663, 25664,
	25773, 25827}
seqDelayUpperBounds = {0, 1,
	0, 0, 0, 0, 
	8, 8}
seqDelayLowerBounds = {0, 0,
	0, 0, 0, 0, 
	0, 0}
--]]

--[[
-- Example 3: Bowser board, turn 2 (from confirming the Bowser candy)
seqActions = {"pYesCandy", "pYesCandy", "pYesCandy", "A", "shake", "shake"}
seqFrames = {42282, 42283, 42284, 42285, 42572, 42627}
seqDelayUpperBounds = {10, 0, 0, 0, 10, 0}
seqDelayLowerBounds = {0, 0, 0, 0, 0, 0}
--]]

---------------------
-- TABLE FUNCTIONS --
---------------------

function tableLength(T)
	local count = 0
	for _ in pairs(T) do count = count + 1 end
	return count
end

function printTable(T)
	local toPrint = "["
	for pos, _ in ipairs(T) do
		toPrint = toPrint .. T[pos] .. ", "
	end
	toPrint = toPrint .. "]"
	SetScreenText(toPrint)
end

function inTable(T, x)
	for _, v in ipairs(T) do
		if v == x then
			return true
		end
	end
	return false
end

function isIncreasing(T)
	local lengthT = tableLength(T)
	for i = 1, lengthT-1 do
		if T[i] >= T[i+1] then
			return false
		end
	end
	return true
end

---------------------------------
-- AUXILLIARY GLOBAL VARIABLES --
---------------------------------
-- No need to modify these.
seqLength = tableLength(seqFrames)

currentSeqFrames = {}
currentSeqDelays = {}

nextSeqComputed = true

isFirstFrame = true
firstFrame = 0

lastSeqReached = false

-- Note that action positions (and table indices in general) start from 1.
currentActionPos = 1

-------------------
-- ACTION MACROS --
-------------------

function Shake()
	SetAccelZ(1023)
end

function PointAt(X,Y)
	SetIRX(X)
	SetIRY(Y)
end

function IsValidAction(action)
	return inTable({"A", "+", "shake", "pNoLow", 
		"pMiddleCandy", "pRightCandy", "pYesCandy", "pOKHotel",
		"pDice", "pYesDKStar", "pSBA", "pWario", "pStartGame"}, action)
end

function ActionToInput(action)
	if not IsValidAction(action) then
		MsgBox("WARNING: unknown action string.")
	-- Frequent actions
	elseif inTable({"A", "+"}, action) then
		PressButton(action)
	elseif action == "shake" then
		Shake()
	-- Situational pointing actions
	elseif action == "pNoLow" then
		-- Points at "No" on low textbox choices.
		-- Examples: shops, or when asked about the secrets of a board.
		PointAt(445, 665)
	elseif action == "pMiddleCandy" then
		-- When carrying one or three candies,
		-- points at the one in the middle.
		PointAt(480, 450)
	elseif action == "pRightCandy" then
		-- When carrying two candies,
		-- points at the one on the right.
		PointAt(400, 450)
	elseif action == "pYesCandy" then
		-- Points at the "Yes" option when confirming to use a candy.
		PointAt(540, 665)
	elseif action == "pDice" then
		-- When carrying at least one candy,
		-- points at the dice block option.
		PointAt(475, 600)
	elseif action == "pYesDKStar" then
		-- Points at the "Yes" option when getting a star on DK's board.
		PointAt(562, 475)
	elseif action == "pOKHotel" then
		-- Points at the "OK" option when investing in a hotel.
		PointAt(280, 650)
	-- Starting menus
	elseif action == "pSBA" then
		-- Points at "Star Battle Arena" in the main menu.
		PointAt(470, 409)
	elseif action == "pWario" then
		-- Points at Wario in the character selection menu.
		PointAt(440, 358)
	elseif action == "pStartGame" then
		-- Points at "Start Game" in the character selection menu.
		PointAt(320, 650)
	end
end

---------------
-- FUNCTIONS --
---------------

-- PRINTING
function FramePrint()
	-- Prints info about the current frame
	local currentFrame = GetFrameCount()
	local toPrint = currentActionPos
	toPrint = toPrint .. " | " .. currentFrame
	if currentActionPos <= seqLength then
		toPrint = toPrint .. string.char(10) .. "Next: " .. currentSeqFrames[currentActionPos]
	end
	-- Table with the action positions at the left,
	-- the current sequence frames in the middle and
	-- the corresponding current delay amounts on the right.
	toPrint = toPrint .. string.char(10) .. "-----------------------"
	toPrint = toPrint .. string.char(10) .. "# | frame | delay |"
	for pos = 1, seqLength do
		toPrint = toPrint .. string.char(10) .. pos .. " | " .. currentSeqFrames[pos] .. " | " .. currentSeqDelays[pos] .. " | "
	end
	toPrint = toPrint .. string.char(10) .. "-----------------------"
	toPrint = toPrint .. string.char(10) .. "(Total delay: " .. currentTotalDelay .. ")"
	SetScreenText(toPrint)
end

function PrintCurrentSeqDelays()
	-- Prints currentTotalDelay and table currentSeqDelays on a single line.
	local toPrint = currentTotalDelay .. " ("
	for pos, delay in ipairs(currentSeqDelays) do
		toPrint = toPrint .. delay
		if pos == seqLength then
			toPrint = toPrint .. ")"
		else
			toPrint = toPrint .. ", "
		end
	end
	SetScreenText(toPrint)
end

-- TESTING
function TestButtons()
	-- Test function to check Wiimote inputs.
	-- It should alternate between A and + presses.
	local currentFrame = GetFrameCount()
	if currentFrame % 2 == 0 then
		PressButton("A")
	else 
		PressButton("+")
	end
end


-- COMPUTING
function InitializeCurrentSeq()
	-- Initializes to the frame sequence with delays at their lower bounds everywhere.
	firstFrame = GetFrameCount()
	seqLength = tableLength(seqFrames)
	currentTotalDelay = 0
	currentSeqFrames = {}
	currentSeqDelays = {}
	for pos, _ in ipairs(seqFrames) do
		currentSeqFrames[pos] = seqFrames[pos]
		for i = 1, pos do
			currentSeqFrames[pos] = currentSeqFrames[pos] + seqDelayLowerBounds[i]
		end
		currentSeqDelays[pos] = seqDelayLowerBounds[pos]
		currentTotalDelay = currentTotalDelay + seqDelayLowerBounds[pos]
	end
end

function ComputeCurrentSeq()
	-- Computes table currentSeqFrames by starting from seqFrames
	-- and adding the values from currentSeqDelays.
	for pos, _ in ipairs(seqFrames) do
		currentSeqFrames[pos] = seqFrames[pos]
		for i = 1, pos do
			currentSeqFrames[pos] = currentSeqFrames[pos] + currentSeqDelays[i]
		end
	end
end

function ComputeNextSeqDelays(pos)
	-- Finds the next sequence of delays (in lexicographic order).
	-- /!\ recursive
	if currentSeqDelays[pos] < math.min(seqDelayUpperBounds[pos], maxTotalDelay) then
		currentSeqDelays[pos] = currentSeqDelays[pos] + 1
		currentTotalDelay = currentTotalDelay + 1
	elseif pos == 1 then
		-- Trying to increment the final sequence.
		-- Then all the requested frame sequences have been considered.
		lastSeqReached = true
	else
		currentTotalDelay = currentTotalDelay - currentSeqDelays[pos] + seqDelayLowerBounds[pos]
		currentSeqDelays[pos] = seqDelayLowerBounds[pos]
		ComputeNextSeqDelays(pos-1)
	end
end

function PrepareForNextSeq()
	-- Finds the next delay sequence (in lexicographic order)
	-- with a total delay higher than or equal to minTotalDelay
	-- and lower than or equal to maxTotalDelay.
	-- /!\ can cancel the script
	repeat
		ComputeNextSeqDelays(seqLength)
	until (currentTotalDelay >= minTotalDelay and currentTotalDelay <= maxTotalDelay) or lastSeqReached
	if lastSeqReached then
		CancelScript()
	else
		ComputeCurrentSeq()
		currentActionPos = 1
	end
end

function SanityChecks()
	-- If you want these sanity checks to be displayed,
	-- make sure to frame advance exactly once after starting the script.
	-- If a save state is loaded between the script start and the frame advance
	-- then the warning messages will still be displayed.
	if seqLength ~= tableLength(seqActions) or 
		seqLength ~= tableLength(seqDelayUpperBounds) or
		seqLength ~= tableLength(seqDelayLowerBounds) then
		SetScreenText("WARNING: input table sizes do not match.")
	elseif not isIncreasing(seqFrames) then
		SetScreenText("WARNING: the given frame sequence is not increasing.")
	else
		for pos = 1, seqLength do
			if not IsValidAction(seqActions[pos]) then
				SetScreenText("WARNING: action number " .. pos .. " is not recognized.")
				return
			end
			if seqDelayLowerBounds[pos] > seqDelayUpperBounds[pos] then
				SetScreenText("WARNING: empty delay range for action number " .. pos .. ".")
				return
			end
		end
	end
end

---------------
-- CALLBACKS --
---------------

function onScriptStart()
	MsgBox("Script started: Mario Party 8 RNG manipulation helper")
	SetScreenText("Script started.")
end

function onScriptCancel()
	MsgBox("Script ended: Mario Party 8 RNG manipulation helper")
	SetScreenText("Script ended.")
end

-- MAIN ROUTINE
-- Overall job: performs the currently tested sequence of delays.
function onScriptUpdate()
	local currentFrame = GetFrameCount()
	if firstFrame == currentFrame then
		SanityChecks()
	else
		isFirstFrame = false
	end
	-- If loading back to the beginning:
	-- test the next delay sequence (in lexicographic order).
	if currentFrame < seqFrames[1] and not nextSeqComputed then
		PrepareForNextSeq()
		nextSeqComputed = true
	elseif currentFrame >= seqFrames[1] and nextSeqComputed then
		nextSeqComputed = false
	end
	-- When going past the final sequence frame, ignore until reloading.
	if currentActionPos > seqLength then
		FramePrint()
		return
	end
	-- When going past a nonfinal sequence frame, update to the next one.
	if currentFrame > currentSeqFrames[currentActionPos] then
		currentActionPos = currentActionPos + 1
	-- When reaching a sequence frame, perform the corresponding action.
	elseif currentFrame == currentSeqFrames[currentActionPos] then
		ActionToInput(seqActions[currentActionPos])
	end
	if not isFirstFrame then
		FramePrint()
	end
end

function onStateLoaded()

end

function onStateSaved()

end

----------
-- MAIN --
----------

InitializeCurrentSeq()