Jump to content

Heavy Lifting


Recommended Posts

hey

I'm looking to make my char have no speed penalty when carrying sculpture pieces and the likes.

tuning.lua says

HEAVY_SPEED_MULT = .15,

equippable.lua reports

local function onwalkspeedmult(self, walkspeedmult)
    if self.inst.replica.inventoryitem ~= nil then
        --This network optimization hack is shared by saddler component,
        --so a prefab must not have both components at the same time.
        self.inst.replica.inventoryitem:SetWalkSpeedMult(walkspeedmult)
    end
end

inventoryitem_replica.lua shows

function InventoryItem:SetWalkSpeedMult(walkspeedmult)
    --V2C: inconsistent precision errors with math.floor
    --     e.g. math.floor(1.15 * 100) => 114 ERMAHGERD
    --     switched to a string solution instead
    --local x = math.floor((walkspeedmult or 1) * 100)
    local x = 100
    if walkspeedmult ~= nil then
        x = tostring(x * walkspeedmult)
        x = tonumber(x:sub(x:find("^%-?%d+")))
    end
    assert(x >= 0 and x <= 255, "Walk speed multiplier out of range: "..tostring(walkspeedmult))
    assert(walkspeedmult == nil or math.abs(walkspeedmult * 100 - x) < .01 , "Walk speed multiplier can only have up to .01 precision: "..tostring(walkspeedmult))
    self.classified.walkspeedmult:set(x)
end

Obviously I don't want this changed for every character, only mine. 

I'm thinking I have to add some postinit in modmain.lua.

As usual I try to AddComponent and then force the commands manually via console in real time to see what I need, however none of these functions alter anything. So I'm thinking it has to do with the sculpture prefabs themselves

sculpting_pieces.lua

inst.components.equippable.walkspeedmult = TUNING.HEAVY_SPEED_MULT

I don't think overwriting a function would do any good here since its the prefab that decides the slowdown? It's not a component so what do i do? Is it even possible to alter a tuning just for 1 character?

Edited by Hell-met
Link to comment
Share on other sites

Take a look at locomotor.lua. It has ServerGetSpeedMultiplier and ClientGetSpeedMultiplier. In the locomotor class declaration, one of those functions are being set to be the GetSpeedMultiplier function for this instance of the locomotor component, depending on whether this is a client or a server instance.

Now, ServerGetSpeedMultiplier uses the equippable component to get the walk speed multiplier, while ClientGetSpeedMultiplier uses the inventoryitem to get the walk speed multiplier. For both of those components they use their public GetWalkSpeedMult function to get the walk speed multiplier from the equipped items.

What you could do is one of two things:

  1. Extend both functions for specific prefabs, by using AddPrefabPostInit, and if the owner/holder of the item is an instance of your character prefab, then if the walkspeed multiplier is less than 1, then return 1.
  2. Extend both functions for all prefabs, by using AddComponentPostInit, and if the owner/holder of the item is an instance of your character prefab, then if the walkspeed multiplier is less than 1, then return 1.

Number 1 leaves you in control over which prefabs this happens to, and you can customize it. Number 2 makes this apply to all prefabs, even ones added by mods, but you might change some items you don't really want to change.

The problem is, it's kind of hard to figure out the owner/holder of an item. I know it's possible, but it's different depending on whether you're a server or client. I've tried to figure it out from the code, but it's kind of a tangled mess.

Sorry, I can't help you get this one to the finishing line.

Link to comment
Share on other sites

I'll just go with the client side

tried this 

AddComponentPostInit("locomotor", function(inst, self)
	local mult = self:ExternalSpeedMultiplier()
	local oldClient = ClientGetSpeedMultiplier(self)
	function ClientGetSpeedMultiplier(self)
		if self.inst.prefab == "CHARHERE" then
			mult = 1
		end
		return oldClient(self)	
	end
end

but doesn't work. Obviously I'm still really bad at this.

Link to comment
Share on other sites

The example you just posted is an example of extending a function. It just means, that instead of overwriting the original function entirely perhaps by copy/pasting from the original game code and making changes to it, you save the original function, then replace it with a new function with your custom code where you make sure that the original function is still called where appropriate. It's all for compatibility purposes. This way, if Klei changes something, or another mod has made changes before your mod is being loaded, then you retain those changes.

Your last code snippet extends the ClientGetSpeedMultiplier, but I'm instead advocating for changing the GetWalkSpeedMult functions in both the inventoryitem and equippable components.

Link to comment
Share on other sites

6 hours ago, Ultroman said:

Exactly!

AddPrefabPostInit( "sculpture_pieces", function(inst)

	if inst.prefab == "namehere" then
	inst.components.equippable.walkspeedmult = 1
	end
	
end)

Should've known it wouldn't be that easy. What's missing here? (apart from "alot")

attempt 2

local function NewCarry(inst)
   if inst.prefab == "namehere" then
   inst.components.equippable.walkspeedmult = 1
   end
end
 
AddPrefabPostInit("sculpture_pieces", NewCarry)

still not quite there

Edited by Hell-met
Link to comment
Share on other sites

You're not extending the function. You're just setting a variable called walkspeedmult. That won't do much, since that variable is change every time the function GetWalkSpeedMult is called, which is the function you should extend. Like you did in your previous code snippet.

AddPrefabPostInit( "sculpture_pieces", function(inst)
	local mult = inst.components.equippable.GetWalkSpeedMult()
	-- Only affect items with a multiplier less than 1
	if mult < 1 then
		-- You are not supposed to include the parentheses when referencing a function, only when you are calling it.
		local oldGetWalkSpeedMult = inst.components.equippable.GetWalkSpeedMult
		-- Overwrite the original function with a new one, but make sure to use the original thereby extending it.
		inst.components.equippable.GetWalkSpeedMult = function(self)
			-- Somehow get the holder/owner of this equippable item.
			local owner = ???

			if owner.prefab == "CHAR_NAME_HERE" then
				return 1.0
			end
			-- If we have not returned yet, then we did not enter the if-statement, so we are not that character.
			-- So we return the value given by the original function.
			return oldGetWalkSpeedMult(self)

			-- There is a LUA shorthand for such things. This is shorthand for the block above. It does the same thing,
			-- but looks a lot weirder ;)
			return owner.prefab == "CHAR_NAME_HERE" and 1.0 or oldGetWalkSpeedMult(self)
		end
	end
end)

I can't remember how to get the owner of an equippable item reliably, and across client and server, from within the component. I know it can be done, though.

Anyway, that's an example for the equippable component. There is something I'm not familiar with, and that's how this whole "classified" stuff works.

Look at inventoryitem_replica. This is its GetWalkSpeedMult function. It uses equippable if it has the component, but if the player is a client(?), then it doesn't have that component, and so it gets it from self.classified.

function InventoryItem:GetWalkSpeedMult()
    if self.inst.components.equippable ~= nil then
        return self.inst.components.equippable:GetWalkSpeedMult()
    elseif self.classified ~= nil then
        return self.classified.walkspeedmult:value() / 100
    else
        return 1
    end
end

 

self.classified.walkspeedmult is set in this function in inventoryitem_replica:

function InventoryItem:SetWalkSpeedMult(walkspeedmult)
    --V2C: inconsistent precision errors with math.floor
    --     e.g. math.floor(1.15 * 100) => 114 ERMAHGERD
    --     switched to a string solution instead
    --local x = math.floor((walkspeedmult or 1) * 100)
    local x = 100
    if walkspeedmult ~= nil then
        x = tostring(x * walkspeedmult)
        x = tonumber(x:sub(x:find("^%-?%d+")))
    end
    assert(x >= 0 and x <= 255, "Walk speed multiplier out of range: "..tostring(walkspeedmult))
    assert(walkspeedmult == nil or math.abs(walkspeedmult * 100 - x) < .01 , "Walk speed multiplier can only have up to .01 precision: "..tostring(walkspeedmult))
    self.classified.walkspeedmult:set(x)
end

...and I have no idea how I'm supposed to respond to that. Do I extend GetWalkSpeedMult() or do I not have to? Or do I need to do something different when it's a replica? I have no idea.

Edited by Ultroman
Link to comment
Share on other sites

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
 Share

×
  • Create New...