Jump to content

Need help with character perk (Unable to kill certain animals)


Recommended Posts

Hello!
I've already made a couple of characters for you and your friend to play (actually, it is a pair of Royal Guards from Undertale)
And I need to add a perk to Royal Guard 01 - making him unable to kill rabbits in any case - if rabbit is on the run or is in the invertory.

Because, you know... RG 01 is a rabbit himself, so he can't kill his own brothers. c:
So now, I've did only two things:
Rabbits aren't afraid of him
He can't kill them with bare hands, he can only examine them

Please, help me, then I will able to add this mod to Workshop c:
Thank you!!!20160429145332_1.jpg 20160428231739_1.jpg

Edited by GearBull
Just adding thanks c:
Link to comment
Share on other sites

3 hours ago, GearBull said:

making him unable to kill rabbits in any case - if rabbit is on the run

Use this on your character prefab file.

local function UpdateCanTarget(inst)
	local _CanTarget = inst.replica.combat.CanTarget
	inst.replica.combat.CanTarget = function(self, target)
		return _CanTarget(self, target) and not target:HasTag("rabbit")
	end
end
local function common_postinit(inst)
	-- Put this line inside common_postinit to apply the UpdateCanTarget function
	inst:DoTaskInTime(0, UpdateCanTarget)
end

Rabbits should be untargetable now.

Link to comment
Share on other sites

Instead of just making rabbits untargetable, could something similar be achieved without discarding the attack option completely?

I would like to have my character a mind of it's own and refuse certain actions given by the player.
It works for cooking and murdering rabbits, where I have a new COOK or MURDER function, which checks for the players prefab and the prefab of the involved object. If it's a rabbit, the function returns false and a data for a custom actionfailure message.

If I do the same with the ATTACK function, the character first runs to the rabbit, attacks with whatever weapon is equipped if close enough but deals no damage (even if the rabbit is sleeping and the character strikes multiple times) and says his custom actionfailure message that is also returned. But also, every couple of seconds the character also says his "attack prey" dialogue line.

It would be better, if the character would just stay where it is and tell the player that the character is not going to hurt the rabbit.
(To be fair, for the cooking part, the character also walks to the fire first. If that behaviour could be eliminated as well, that would be awesome.)

Here is the code I am using:
 

old_cook_fn = GLOBAL.ACTIONS.COOK.fn
function GLOBAL.ACTIONS.COOK.fn(act)
	print (act.doer)
	print (act.doer.prefab)
	if (act.doer ~= nil and act.doer.prefab == "jessica") then
		-- do either  return false  if the action should fail, or  return old_cook_fn(act)  if the target can be cooked
		print (tostring(act.invobject))
		if act.invobject ~= nil and act.invobject.prefab == "rabbit" then
			return false, "NOCOOKRABBIT" -- or whatever the string is identified as (ACTIONFAIL.COOK.NOCOOKRABBIT)
		else
			return old_cook_fn(act)
		end
	else
		return old_cook_fn(act)
	end
end
-------------------------------------------------------------------------------
old_murder_fn = GLOBAL.ACTIONS.MURDER.fn
function GLOBAL.ACTIONS.MURDER.fn(act)
	print (act.doer)
	print (act.doer.prefab)
	if (act.doer ~= nil and act.doer.prefab == "jessica") then
		-- do either  return false  if the action should fail, or  return old_murder_fn(act)  if the target can be murdered
		print (tostring(act.invobject))
		if act.invobject ~= nil and act.invobject.prefab == "rabbit" then
			return false, "NOMURDERRABBIT" -- (ACTIONFAIL.MURDER.NOMURDERRABBIT)
		else
			return old_murder_fn(act)
		end
	else
		return old_murder_fn(act)
	end
end
-------------------------------------------------------------------------------
old_attack_fn = GLOBAL.ACTIONS.ATTACK.fn
function GLOBAL.ACTIONS.ATTACK.fn(act)
	print (act.doer)
	print (act.doer.prefab)
	if (act.doer ~= nil and act.doer.prefab == "jessica") then
		-- do either  return false  if the action should fail, or  return old_attack_fn(act)  if the target can be attacked
		print (tostring(act.target))
		if act.target ~= nil and act.target.prefab == "rabbit" then
			return false, "NOATTACKRABBIT" -- (ACTIONFAIL.ATTACK.NOATTACKRABBIT)
		else
			return old_attack_fn(act)
		end
	else
		return old_attack_fn(act)
	end
end

 

Link to comment
Share on other sites

3 hours ago, KainMorgen said:

(To be fair, for the cooking part, the character also walks to the fire first. If that behaviour could be eliminated as well, that would be awesome.)

Honestly everything was easy except for this god forsaken part.

Curse movement prediction.


local REASONS = {
	NOCOOKRABBIT = "NOCOOKRABBIT",
	NOMURDERRABBIT = "NOMURDERRABBIT",
	NOATTACKRABBIT = "NOATTACKRABBIT",
}

local ACTIONS = GLOBAL.ACTIONS

local function BufferedActionEvaluation(ba)
	local act = ba.action
	local reason = nil
	if act == ACTIONS.COOK then
		local victim = ba.invobject and ba.invobject.prefab
		if victim == "rabbit" then
			reason = "NOCOOKRABBIT"
		end
	elseif act == ACTIONS.MURDER then
		local victim = ba.invobject and ba.invobject.prefab
		if victim == "rabbit" then
			reason = "NOMURDERRABBIT"
		end
	elseif act == ACTIONS.ATTACK then
		local victim = ba.target and ba.target.prefab
		if victim == "rabbit" then
			reason = "NOATTACKRABBIT"
		end
	end
	return reason == nil, reason
end

local BufferedAction = GLOBAL.BufferedAction

local _TestForStart = BufferedAction.TestForStart

BufferedAction.TestForStart = function(self)
	if self.doer and self.doer:HasTag("rabbitmercy") then
		local success, reason = _TestForStart(self)
		local mysuccess, myreason = BufferedActionEvaluation(self)
		success = success and mysuccess
		reason = myreason or reason
		return success, reason
	end
	return _TestForStart(self)
end

-- For the people that enables movement prediction...
AddComponentPostInit("locomotor", function(self)
	if self.inst:HasTag("rabbitmercy") then
		local _PreviewAction = self.PreviewAction
		self.PreviewAction = function(self, bufferedaction, run, try_instant)
			if bufferedaction == nil then
				return
			end
			local success, reason = bufferedaction:TestForStart()
			if not success and REASONS[reason] ~= nil then
				self.inst.bufferedaction = bufferedaction
				self.inst:PerformPreviewBufferedAction()
				return
			end
			_PreviewAction(self, bufferedaction, run, try_instant)
		end
	end
end)

But here you go.

Add the "rabbitmercy" tag to your common_postinit.

Link to comment
Share on other sites

I've already completed that mod, but yeah, perhaps this would be even better.

If my new thread is up again (instead of new one), may I ask about Sanity (Or damage bust) aura, when two sertain characters are together? Is it possible to be made?

Link to comment
Share on other sites

Thank you very much, DarkXero!

This code is exactly what I was looking for and it is working fine. :) 
And for the best part: while I couldn't have written that down myself, I think I understand what you did there and I will be able to further enhance it in case of I get more ideas.

Link to comment
Share on other sites

12 hours ago, GearBull said:

may I ask about Sanity (Or damage bust) aura, when two sertain characters are together? Is it possible to be made?


inst.components.combat.damagemultiplier = 1

inst.aura_activated = false

inst:DoPeriodicTask(1, function(inst)
	local dist_sq = 10 * 10
	local should_aura = false
	for i, v in ipairs(AllPlayers) do
		if v:HasTag("compadre") and v:GetDistanceSqToInst(inst) < dist_sq then
			should_aura = true
			break
		end
	end
	if should_aura then
		if not inst.aura_activated then
			inst.aura_activated = true
			inst.components.sanity.dapperness = inst.components.sanity.dapperness + TUNING.DAPPERNESS_MED
			inst.components.combat.damagemultiplier = inst.components.combat.damagemultiplier * 2
		end
	else
		if inst.aura_activated then
			inst.aura_activated = false
			inst.components.sanity.dapperness = inst.components.sanity.dapperness - TUNING.DAPPERNESS_MED
			inst.components.combat.damagemultiplier = inst.components.combat.damagemultiplier / 2
		end
	end
end)

A character with this on its master_postinit will gain tophat sanity and 2x damage multiplier while standing near a player that has the "compadre" tag. So you can have two characters with this code, have one with "compadre_one" and the other with "compadre_two", change "compadre" to one of those, respectively, and they will benefit from standing close.

Link to comment
Share on other sites

I try to do this but not work from me. I'm new at coding what's wrong

 


local MakePlayerCharacter = require "prefabs/player_common"


local assets = {

        Asset( "ANIM", "anim/player_basic.zip" ),
        Asset( "ANIM", "anim/player_idles_shiver.zip" ),
        Asset( "ANIM", "anim/player_actions.zip" ),
        Asset( "ANIM", "anim/player_actions_axe.zip" ),
        Asset( "ANIM", "anim/player_actions_pickaxe.zip" ),
        Asset( "ANIM", "anim/player_actions_shovel.zip" ),
        Asset( "ANIM", "anim/player_actions_blowdart.zip" ),
        Asset( "ANIM", "anim/player_actions_eat.zip" ),
        Asset( "ANIM", "anim/player_actions_item.zip" ),
        Asset( "ANIM", "anim/player_actions_uniqueitem.zip" ),
        Asset( "ANIM", "anim/player_actions_bugnet.zip" ),
        Asset( "ANIM", "anim/player_actions_fishing.zip" ),
        Asset( "ANIM", "anim/player_actions_boomerang.zip" ),
        Asset( "ANIM", "anim/player_bush_hat.zip" ),
        Asset( "ANIM", "anim/player_attacks.zip" ),
        Asset( "ANIM", "anim/player_idles.zip" ),
        Asset( "ANIM", "anim/player_rebirth.zip" ),
        Asset( "ANIM", "anim/player_jump.zip" ),
        Asset( "ANIM", "anim/player_amulet_resurrect.zip" ),
        Asset( "ANIM", "anim/player_teleport.zip" ),
        Asset( "ANIM", "anim/wilson_fx.zip" ),
        Asset( "ANIM", "anim/player_one_man_band.zip" ),
        Asset( "ANIM", "anim/shadow_hands.zip" ),
        Asset( "SOUND", "sound/sfx.fsb" ),
        Asset( "SOUND", "sound/wilson.fsb" ),
        Asset( "ANIM", "anim/beard.zip" ),

        Asset( "ANIM", "anim/azura.zip" ),
}
local prefabs = {}
local start_inv = {
    -- Custom starting items
}
local function UpdateCanTarget(inst)
    local _CanTarget = inst.replica.combat.CanTarget
    inst.replica.combat.CanTarget = function(self, target)
        return _CanTarget(self, target) and not target:HasTag("rabbit")
    end
end

local function common_postinit(inst)
    -- Put this line inside common_postinit to apply the UpdateCanTarget function
    inst:DoTaskInTime(0, UpdateCanTarget)
end

local fn = function(inst)
    
    -- choose which sounds this character will play
    inst.soundsname = "wendy"

    -- Minimap icon
    inst.MiniMapEntity:SetIcon( "azura.tex" )
    
    -- Stats    
    inst.components.health:SetMaxHealth(200)
    inst.components.hunger:SetMax(120)
    inst.components.sanity:SetMax(200)
    
    -- Damage multiplier (optional)
    inst.components.combat.damagemultiplier = 1
    
    -- Hunger rate (optional)
    inst.components.hunger.hungerrate = 1 * TUNING.WILSON_HUNGER_RATE
    
    -- Movement speed (optional)
    inst.components.locomotor.walkspeed = 4
    inst.components.locomotor.runspeed = 6
end

return MakePlayerCharacter("azura", prefabs, assets, fn, start_inv)
 

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