Jump to content

Recommended Posts

I am trying to make a simple client mod that removes Wes from the pool of random characters. I have:

AddClassPostConstruct("screens/redux/lobbyscreen", SelectRandomCharacter)

since "screens/lobbyscreen" didn't result in any functions being executed. However, I cannot find the name of the random button, and I am unfamiliar with how AddClassPostConstruct works.

Old solution (leaving it here to illustrate my process) I decided to annotate it because you said you wanted help modding:

Spoiler
Spoiler
local IsCharacterOwned_old = GLOBAL.IsCharacterOwned -- Almost any global function can be overwritten. We are using _old to declare this as a base for our hook
local lockedlist = {}
local function LockCharacter(data) -- Holding a fn hook for IsCharacterOwned for later (a fn hook is basically adding onto an already existing fn)
	if type(data) == "string" then -- if we only have a string as our data, add it to the lockedlist
		table.insert(lockedlist, data)
	else
		for _,character in pairs(data) do -- if we have a table as an argument, annex the whole table to the lockedlist
			table.insert(lockedlist, character)
		end
	end
 	-- if you want to make sure people aren't misuing the function you can do GLOBAL.assert(type(data) == "string" or type(data) == "table", "Must be string or table") but i think the usecase for this fn is obvious enough
	GLOBAL.IsCharacterOwned = function(character) -- rewriting the global IsCharacterOwned fn
		for _,lockedchar in pairs(lockedlist) do -- adding a new check and ending the function with false early if we have a character we want to not be in the random pool
			if character == lockedchar then
				return false
			end
		end
		return IsCharacterOwned_old(character) -- returning and executing the old fn's results
	end
end

local function UnlockCharacters() -- resetting the table and IsCharacterOwned fn to it's original state
	lockedlist = {}
	GLOBAL.IsCharacterOwned = IsCharacterOwned_old
end

AddClassPostConstruct("screens/redux/lobbyscreen", function(self)
	local OnControl_old = self.OnControl
	self.OnControl = function(self, ...) -- using self.OnControl as a listener. this will be explained later
		local character = self.lobbycharacter -- print(character) -- currently selected character 
		if character == "random" then
			LockCharacter("wes")
		end
		if character ~= "random" then
			UnlockCharacters()
		end
		local result = OnControl_old(self, ...)
		if character == "random" then
			UnlockCharacters()
		end
		return result
	end
end)

 

This works, but the main issue with this solution was If you select random but go back, the character will show as locked, but you can still select it.

Unfortunately doing the same thing with GLOBAL.MODCHARACTEREXCEPTIONS_DST makes the characters removed permanently once random is selected, making it a worse option for this.

New solution that I found no issues with (use this): You told me you wanted help understanding modding, so I decided to annotate it

Spoiler
local IsCharacterOwned_old = GLOBAL.IsCharacterOwned
local lockedlist = {}
local function LockCharacter(data)
	if type(data) == "string" then
		table.insert(lockedlist, data)
	else
		for _,character in pairs(data) do
			table.insert(lockedlist, character)
		end
	end
	GLOBAL.IsCharacterOwned = function(character)
		for _,lockedchar in pairs(lockedlist) do
			if character == lockedchar then
				return false
			end
		end
		return IsCharacterOwned_old(character)
	end
end

local function UnlockCharacters()
	lockedlist = {}
	GLOBAL.IsCharacterOwned = IsCharacterOwned_old
end

local function NewNextButton(self) -- self is meant to be the LoadoutPanel
	local OnNextButton_old = self.OnNextButton
	self.OnNextButton = function(self, ...)
		LockCharacter("wes") -- ante-hooking the locked character state so the random result is different. OnNextButton uses IsCharacterOwned as one of its determiners to which character is selected. locked characters will not be selected.
		local result = OnNextButton_old(self, ...) -- saving the result of the original next button function and executing it, by the time this is done, the randomization has been done.
		UnlockCharacters() -- we are post-hooking OnNextButton because we want the locked character check to be the same as before right after the nextbutton fn is dealt with 
		return result -- now we can finish the function and return the result
	end
end

AddClassPostConstruct("screens/redux/lobbyscreen", function(self)
	local OnControl_old = self.OnControl
	self.OnControl = function(self, ...) -- we are using OnControl as a listener, because if we aren't controlling in the menu, no point in overwriting anything
		if tostring(self.panel) == "LoadoutPanel" and self.lobbycharacter == "random" then -- getting the current panel name and selected lobby character
			NewNextButton(self.panel) -- we must overwrite the next button each time we encounter it, otherwise the LoadoutPanel fn could be different
		end
		return OnControl_old(self, ...) -- execute the original function and return it at the same time. generally good practice for fn hooks is returning the original function's result, but it's not always necessary. since this returns an actual result, it's crucial, though
	end
end)

 

I would like to use CHARACTEREXCEPTIONS but because of the possibility of using a moon rock idol and the characters not being reloaded, I have to stick with IsCharacterLocked instead. I couldn't find a way to overwrite the panel directly so I listened for when the current panel is LoadoutPanel.

 If you want to test if it really works, do this. The expected result is that you are Wigfrid each time.

LockCharacter({"wes", "wortox", "willow", "wilson", "wendy", "waxwell", "wanda", "wolfgang", 
"wx78", "wickerbottom", "woodie", "warly", "webber", "winona", "wormwood", "wurt", "walter"})
	
Edited by oregu

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
  • Create New...