Jump to content

GetLoyaltyPercent


Recommended Posts

I'm using "Pet Follower Badges Together Icon" to test a mod I'm building, where the character can train hounds.
Normally, asking for a follower's percent-loyalty is easy, but mine seems to be buggy..

function Follower:GetLoyaltyPercent()
    if self.targettime ~= nil and self.maxfollowtime ~= nil then
        local timeLeft = math.max(0, self.targettime - GetTime())
        return timeLeft / self.maxfollowtime
    end
    return 0
end

My code doesn't produce a basic percent value, but a super high value.  I think it's the number of frames of loyalty left, but I'm not sure.
 

if GetModConfigData("packleader") == 1 then

-- Postinit for all hounds
local function AddHoundPostInit(fn)
	AddPrefabPostInitAny( function(inst)
		if inst and inst:HasTag("hound") and not inst:HasTag("clay") then  --Only non-clay hounds can be trained.  Wargs can not be trained.  Note: Horror hounds can be trained.
			fn(inst)
		--	inst.OnSave = onsave  --Test code.  Note: Currently unused.
		--	inst.OnLoad = onload
		end
	end)
end

-- Function to remove sanity aura  --Note: I believe this grants a negative sanity aura immunity to 'hound tamers'.  Note: Testing suggests that the aura of tamed hounds will not affect any player.
local removeSanityAura = function(inst)
	if inst.components.sanityaura then
		local aurafn_orig = inst.components.sanityaura.aurafn
		local aurafn_new = function(inst, observer)
			-- Check if the observer is a friend of hounds
			if observer:HasTag("houndtamer") then
				return 0
			end
			if aurafn_orig then
				return aurafn_orig(inst, observer)
			end
			return inst.components.sanityaura.aura
		end
		inst.components.sanityaura.aurafn = aurafn_new
	end
end

AddHoundPostInit(removeSanityAura)

----------------------------------------------------
-- BEFRIENDING HOUNDS

local HOUND_LOYALTY_PER_HUNGER = TUNING.TOTAL_DAY_TIME / 25  --A single dried meat will get you about a day of loyalty.
local HOUND_LOYALTY_MAXTIME = 2.5 * TUNING.TOTAL_DAY_TIME


local makeHoundFriend = function(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return
	end

	-- Requires the follower component
	if not inst.components.follower then
		return
	end

	-- Add the trader component if not already added
	if not inst.components.trader then
		inst:AddComponent("trader")
	end

	-- Add a new test function to whether they should accept or not
	local test_prev = inst.components.trader.test
	local test_new = function(inst, item, giver)
		if not giver:HasTag("houndtamer") then
			-- Do whatever function it was before
			if test_prev then
				return test_prev(inst, item, giver)
			end
			return
		end

		if inst.components.eater:CanEat(item) then
			return true
		end
	end
    inst.components.trader:SetAcceptTest(test_new)

	local onaccept_prev = inst.components.trader.onaccept
	local onaccept_new = function(inst, giver, item)
		if not giver:HasTag("houndtamer") then
			-- Do whatever function it was before
			if onaccept_prev then
				return onaccept_prev(inst, giver, item)
			end
			return
		end
		if inst.components.eater:CanEat(item) then  

			local playedfriendsfx = false
			if inst.components.combat.target and inst.components.combat.target == giver then
				inst.components.combat:SetTarget(nil)
			elseif giver.components.leader then  --What happens if the hound can be trained.
				inst.SoundEmitter:PlaySound("dontstarve/common/makeFriend")
				playedfriendsfx = true
				giver.components.leader:AddFollower(inst)  --Note: Value is added to the player, not the hound?
				local loyaltyTime = item.components.edible:GetHunger() * HOUND_LOYALTY_PER_HUNGER
				inst.components.follower:AddLoyaltyTime(loyaltyTime)  --This is used to determine how long the hound will be loyal for.
			--	if inst.component.health.current < 150 then  --Test code.  Hounds heal when fed.  Note: Causes the mod to break.
			--		inst.components.health.current == inst.components.health.current + 10
			--	end
			end

		end
	end
    inst.components.trader.onaccept = onaccept_new

	local onrefuse_prev = inst.components.onrefuse
	local function onrefuse_new(inst, giver, item)
		if not giver:HasTag("houndtamer") then
			-- Do whatever function it was before
			if onrefuse_prev then
				return onrefuse_prev(inst, giver, item)
			end
			return
		end
		inst.sg:GoToState("taunt")
		if inst.components.sleeper:IsAsleep() then
			inst.components.sleeper:WakeUp()
		end
	end
    inst.components.trader.onrefuse = onrefuse_new


	-- Add loyalty in a way that will not conflict with other mods
	local follower = inst.components.follower
	local AddLoyaltyTime_prev = follower.AddLoyaltyTime
	function follower:AddLoyaltyTime(time)
		if follower.leader and follower.leader:HasTag("houndtamer") then  --Note: The hound 'AddLoyaltyTime' component is used to trigged a stop following event.
			local currentTime = GLOBAL.GetTime()
			local timeLeft = self.targettime or 0
			timeLeft = math.max(0, timeLeft - currentTime)
			timeLeft = math.min(HOUND_LOYALTY_MAXTIME or 0, timeLeft + time)
			
			self.targettime = currentTime + timeLeft

			if self.task then
				self.task:Cancel()
				self.task = nil
			end
			self.task = self.inst:DoTaskInTime(timeLeft, stopfollow)  --Causes the hound to stop following the player, becoming wild again.
		--	self.task = self.inst:DoTaskInTime(timeLeft, inst:RemoveTag("pack"))  --Test code.
		else
			return AddLoyaltyTime_prev(self, time)
		end
	end

	if inst.components.combat then
		-- Prevent hounds from attacking other players
		local targetfn_orig = inst.components.combat.targetfn
		local targetfn_new = function(inst)
			local target = nil
			-- Choose target normally
			if targetfn_orig then
				target = targetfn_orig(inst)
			end
			-- Check if this hound has a hound tamer as a leader.
			if target ~= nil and inst.components.follower and inst.components.follower.leader and inst.components.follower.leader:HasTag("houndtamer") then
				-- Check if target should be kept
				if target:HasTag("player")
				or target:HasTag("companion")
				then
					target = nil
				end
			end

			return target
		end
		inst.components.combat.targetfn = targetfn_new

		local ShareTarget_prev = inst.components.combat.ShareTarget
		function inst.components.combat:ShareTarget(target, ...)
			-- Share target normally if being led by someone other than hound tamer.
			if inst.components.follower and inst.components.follower.leader and not inst.components.follower.leader:HasTag("houndtamer") then
				return ShareTarget_prev(self, target, ...)
			end
			-- Prevents hound tamers from being shared as a target.
			if target:HasTag("houndtamer") then
				return
			end
			-- Prevents players and companions from being shared as a target.
			if target:HasTag("player")
			or target:HasTag("companion")
			then
				local houndtamer = GLOBAL.FindEntity(inst, 20, function(guy)
					return guy:HasTag("houndtamer")
				end)
				if houndtamer ~= nil then
					return
				end
			end
			return ShareTarget_prev(self, target, ...)
		end

	end
end

AddHoundPostInit(makeHoundFriend)

I think I'm missing 'maxfollowtime' but I'm not exactly sure..

Link to comment
Share on other sites

@penguin0616
image.thumb.png.58c795a3ca7cdb4a9a67894409776c18.png

Keep getting this issue..
 

local inst.components.follower.maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME
local inst.components.follower.maxfollowtime == 2.5 * TUNING.TOTAL_DAY_TIME
local inst.components.follower:maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME
local inst.components.follower:maxfollowtime == 2.5 * TUNING.TOTAL_DAY_TIME

 

Link to comment
Share on other sites

@penguin0616
That's just a list of what I've tried.  I expected one of them to work, but none did; all produced the same exact error.

image.thumb.png.04155e1108185a2f96b57d77a05717aa.png

Same error with this code..

local inst.maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME  --Test code.

Edit:
I might have found a solution, but it's going to take me some time to get it working, and my current attempts keep producing the same issue as before..  

--Winifred Follower Time 
AddComponentPostInit("follower", function(self)
    local _AddLoyalityTime = self.AddLoyaltyTime
    function self:AddLoyaltyTime(time, ...)
        if self.leader and self.leader:HasTag("veryfriendly") and GLOBAL.GetTime() > 0 then
		    self.maxfollowtime = 99999
            time = time * 100 -- TUNE HERE ( you can also add absolute values instead of multiplying)
        end
        _AddLoyalityTime(self, time, ...)
    end
end
Edited by FurryEskimo
Link to comment
Share on other sites

@penguin0616
image.thumb.png.80ccca52de3a321f2fcffff8ccdd6723.png

 

-- Postinit for all hounds
local function AddHoundPostInit(fn)
	AddPrefabPostInitAny( function(inst)
		if inst and inst:HasTag("hound") and not inst:HasTag("clay") then  --Only non-clay hounds can be trained.  Wargs can not be trained.  Note: Horror hounds can be trained.
			fn(inst)
			inst.components.follower.maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME  --Test code.
		end
	end)
end

Wow, it Really hated that!  That's a whole new kind of error.
(More details from server log incoming)

Edited by FurryEskimo
Link to comment
Share on other sites

@penguin0616
As for the server log, which one did you want to see?  The one where I use "inst.components.follower.maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME" outside the postinit is attached.  The one where it's inside the postinit does not appear to create a log, as the entire game simply crashes.

server_log.txt

Edited by FurryEskimo
Link to comment
Share on other sites

Perhaps this will do it.

function houndPost(inst)
	if not inst.components.follower then
		inst:AddComponent("follower")
	end
	inst.components.follower.maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME 
end

AddPrefabPostInitAny(function(inst)
	if inst:HasTag("hound") and not inst:HasTag("clay") then
		houndPost(inst)
	end
end
  • Like 1
Link to comment
Share on other sites

@penguin0616
The server did launch, but it appears the mod is broken, as I was sent back to the select screen and the modded character was not available.  This was the edit I made:

-- Postinit for all hounds
--[[  --Origninal code.
local function AddHoundPostInit(fn)
	AddPrefabPostInitAny( function(inst)
		if inst and inst:HasTag("hound") and not inst:HasTag("clay") then  --Only non-clay hounds can be trained.  Wargs can not be trained.  Note: Horror hounds can be trained.
			fn(inst)
		end
	end)
end
--]]

--Your code.
function houndPost(inst)  --Test code.
	if not inst.components.follower then
		inst:AddComponent("follower")
	end
	inst.components.follower.maxfollowtime = 2.5 * TUNING.TOTAL_DAY_TIME 
end

AddPrefabPostInitAny(function(inst)
	if inst:HasTag("hound") and not inst:HasTag("clay") then
		houndPost(inst)
	end
end

If it would help, here's a link to the mod's unlisted beta page.  https://steamcommunity.com/sharedfiles/filedetails/?id=2238207705
I've also attached the modmain.  The code in question is on line 1,468.

modmain.lua

Edited by FurryEskimo
Link to comment
Share on other sites

@penguin0616
Ok, that does seem to be working, thanks.  This did reveal a weird new issue though.  I can test the loyalty per hunger value, and it's exactly what it should be, but for some reason any food seems, seems to max out a hound's loyalty.  I'll need to figure out what's wrong.  I'm still using the Per Follower Badges mod, which should automatically display the loyalty values, but it's consistently wrong, but does seem to change when I edit the max loyalty time.

Edited by FurryEskimo
Link to comment
Share on other sites

Edit:
It seems like the hound's loyalty value is set, and the maximum value, but the percent is consistently screwy.
When I fed a hound a morsel:

[00:00:54]: Hound Loyalty Per Hunger	
[00:00:54]: 19.2	
[00:00:54]: Total Day Time	
[00:00:54]: 480	
[00:00:54]: Food Item's Hunger Value	
[00:00:54]: 12.5	
[00:00:54]: Loyalty Time	
[00:00:54]: 240	
[00:00:54]: Hound Loyalty MaxTime	
[00:00:54]: 480	
[00:00:54]: Loyalty Percent	
[00:00:54]: 8	

Fed a hound a veggie-burger:

[00:01:06]: Hound Loyalty Per Hunger	
[00:01:06]: 19.2	
[00:01:06]: Total Day Time	
[00:01:06]: 480	
[00:01:06]: Food Item's Hunger Value	
[00:01:06]: 37.5	
[00:01:06]: Loyalty Time	
[00:01:06]: 720	
[00:01:06]: Hound Loyalty MaxTime	
[00:01:06]: 480	
[00:01:06]: Loyalty Percent	
[00:01:06]: 8	

For some reason the percent loyalty keeps registering as 800%.

Boosting the max follow time to 2.5 days causes further weirdness with the loyalty percent, even though the actual loyalty times are exactly what I expect them to be.

[00:00:58]: Hound Loyalty Per Hunger	
[00:00:58]: 19.2	
[00:00:58]: Total Day Time	
[00:00:58]: 480	
[00:00:58]: Food Item's Hunger Value	
[00:00:58]: 12.5	
[00:00:58]: Loyalty Time	
[00:00:58]: 240	
[00:00:58]: Hound Loyalty MaxTime	
[00:00:58]: 1200	
[00:00:58]: Loyalty Percent	
[00:00:58]: 11.469444436766	

 

[00:01:09]: Hound Loyalty Per Hunger	
[00:01:09]: 19.2	
[00:01:09]: Total Day Time	
[00:01:09]: 480	
[00:01:09]: Food Item's Hunger Value	
[00:01:09]: 37.5	
[00:01:09]: Loyalty Time	
[00:01:09]: 720	
[00:01:09]: Hound Loyalty MaxTime	
[00:01:09]: 1200	
[00:01:09]: Loyalty Percent	
[00:01:09]: 19.486111093871	

Idk how to add inspect text for entities, like you appear to be able to do.

Edited by FurryEskimo
Link to comment
Share on other sites

@penguin0616
I removed the Follower:AddLoyaltyTime code, which I think means anyone can train the hounds now (idk, it might have been redundant), and the percent value is mostly correct now.

[00:02:35]: Hound Loyalty Per Hunger	
[00:02:35]: 19.2	
[00:02:35]: Total Day Time	
[00:02:35]: 480	
[00:02:35]: Food Item's Hunger Value	
[00:02:35]: 12.5	
[00:02:35]: Loyalty Time	
[00:02:35]: 240	
[00:02:35]: Hound Loyalty MaxTime	
[00:02:35]: 1200	
[00:02:35]: Loyalty Percent	
[00:02:35]: 1	

It's displaying 100% loyalty, when it should be displaying 20%. The amount of loyalty the hounds do have seems to last about 1/8th of a day.  I'll look into this more later today, but for now I've got some yard work to get done.  I really appreciate the help.

Link to comment
Share on other sites

@penguin0616
Is that so?  It's been a while since I went poking through this code, but there're checks for 'houndtamer' tags all over.  This is the code I believe prevents non-hound trainers from training hounds:

	-- Add a new test function to whether they should accept or not
	local test_prev = inst.components.trader.test
	local test_new = function(inst, item, giver)
		if not giver:HasTag("houndtamer") then
			-- Do whatever function it was before
			if test_prev then
				return test_prev(inst, item, giver)
			end
			return
		end

		if inst.components.eater:CanEat(item) then
			return true
		end
	end

I know the hounds will target non-hound trainers, so even though you can try giving the hounds items, they're too busy attacking you to accept the trade.

Link to comment
Share on other sites

@penguin0616
This feels like a catch 22.  The original code trained hounds for the correct amount of time, but the percentages were wildly incorrect.  The edits so far cause the percentage to stay under 100%, but the time the hound is trained is incorrect and the percentage is wrong.  Geez, you'd think these would be easy edits.  All the values are defined and the training seems to work, so why doesn't the percentage?  smh

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...