Jump to content

Character Perk Creation Part 2, Assistance appreciated


Recommended Posts

Okay, got my first two characters built and functioning.  Yay, thank you everyone on the forum for your wonderous help.

 

Once my artist finishes the sprite work I'll post my work for everyone to enjoy.

 

In the meantime I'm ready to tackle a character design that sounds a little more daunting, this one especially is going to have a multiplayer influence so I feel it's appropriate for the DST mod forum.

 

My first two characters were made in DST and I'm pretty familiar with how the network code works but this character is going to have some actual features that will depend on what other players do.

 

 

My next character is going to involve a couple concepts around keeping rabbits alive.  A friend of mine actually plays this way normally and foregoes using rabbits as a source of meat.  Which inspired for this characters creation.

 

Essentially, the character is going to have a perk where depending on the number of rabbits in her inventory, she will get a random item (from a table) from each rabbit.  Items that don't fit in the inventory spill onto the ground.  This will only happen if no rabbits are killed ANYWHERE on the server for 3 days (either by time units or just by counting up by 1 everytime a new day starts)

 

If a rabbit (not bunnyman) dies during these three days but before gift day, it resets the whole counter making it start over, and this character loses sanity (She knows when a bunny dies).

 

 

 

Now I already have a general idea on how to get this character's main perks built, for the rabbits giving gifts I'll run a function that counts each rabbit in the inventory then loop a gift function for the count that's given (Don't know the exact code for that at the moment...)

 

Also I'll need a variable thats storing the amount of days that have gone by while rabbits are sitting in the inventory (Still deciding between individual timers per rabbit, or one single timer.  But the individual timer sounds better, is there a way I can store a variable on the individual rabbits?)

 

Also the sanity drain from the detection of a rabbit dying on the server.  I'm thinking of using the AllPlayers[] function to select each player in a loop and perform actions on each one that use my characters prefab, resetting the gift timer and causing a sanity drain.

 

 

The theory behind the code for my character sounds like it should work, but I don't know if there might be any obstacles I'm going to run into involving interactions with other players.

 

Furthermore, I'm not sure how optimal my ideas are, if I'm doing something inefficient let me know, this is a learning experience above all else.

 

 

I'll post back with my code after I've written it.

 

 

Edit:

 

Another perk I want to try and get working is removing the characters ability to attack and murder rabbits and bunnymen.  This involves adjusting actions which has given me trouble in the past so any assistance on it would be appreciated.

Edited by Zackreaver
Link to comment
Share on other sites

@Zackreaver, I think the approaches you outlined would work, but there's a much better way to go about it, by using the existing event framework in the game. Events are a very powerful model that allow for much more efficient code in certain circumstances, I highly recommend reading up on it sometime (I wish I had a good reference to point you toward, but I learned it pretty haphazardly myself, so... but here's the Wikipedia article on it.)

 

In your character's prefab (the master_postinit, this shouldn't run on the client):

inst:ListenForEvent("entity_death", function(inst, data)    if data and data.inst and data.inst.prefab == "rabbit" then        inst.components.sanity:DoDelta(-5)    endend)

In the modmain:

local gifts = { "twigs", "cutgrass", "log", "seeds" } -- add whatever you wantlocal GIFT_DURATION = 3 * GLOBAL.TUNING.TOTAL_DAY_TIMEif GLOBAL.TheNet:GetIsServer() then	local function makegift(inst)		if inst.components.inventoryitem.owner then			local gift = SpawnPrefab(gifts[math.random(#gifts)])			inst.components.inventoryitem.owner.components.inventory:GiveItem(gift)			inst.gift_task = inst:DoTaskInTime(GIFT_DURATION, makegift)		end	end		local function topocket(inst, owner)		if owner.prefab == "mychar" then			inst.gift_task = inst:DoTaskInTime(GIFT_DURATION, makegift)		end	end		local function toground(inst)		if inst.gift_task then			inst.gift_task:Cancel()			inst.gift_task = nil		end	end		AddPrefabPostInit("rabbit", function(inst)		inst:ListenForEvent("onputininventory", topocket)		inst:ListenForEvent("ondropped", toground)		inst:ListenForEvent("entity_death", function(inst, data)			if inst.gift_task and data and data.inst and data.inst.prefab == "rabbit" then				inst.gift_task:Cancel()				inst.gift_task = inst:DoTaskInTime(GIFT_DURATION, makegift)			end		end)	end)end

Edit: Fixed "entitydeath" -> "entity_death"

Edited by rezecib
Link to comment
Share on other sites

@rezecib, I'm quite aware of it actually, just getting used to working with don't starve together, I figure everybody here has been working with everything here enough that they know the best methods to use.

 

I feel kinda bad now, you just saved me alot of work lol.  But I'm pretty damn impressed, I wasn't expecting the concept's code to be so... clean.  But so far I'm really loving modding in Don't Starve, everything has been really easy to figure out, and everybody on the forum is being so helpful as well.

 

Thanks.

 

Edit: It seems everytime I get help from you rezecib, I learn something about lua and the mod tools that I never realized.

Edited by Zackreaver
Link to comment
Share on other sites

@rezecib, inst:ListenForEvent("entitydeath") doesn't seem to work.  Was it supposed to be "entity_death"? because that doesn't seem to work either, it came up on FindinFile as a world event, I don't think characters can listen for those events.  My debug print functions doesn't even trigger.

 

Edit: Okay now this part is strange, when SpawnPrefab is called in modmain, it comes up as nil value.  Not the prefab it's trying to make, just the function SpawnPrefab itself.

Edited by Zackreaver
Link to comment
Share on other sites

Yeah, Don't Starve modding is great. It really made me appreciate a lot of the features of languages like Lua :D.

 

inst:ListenForEvent("entitydeath") doesn't seem to work.  Was it supposed to be "entity_death"?

 That's correct, I messed that up.

 

 

 

Okay now this part is strange, when SpawnPrefab is called in modmain,

Oops, that's in the GLOBAL space. So GLOBAL.SpawnPrefab(gift).

Edited by rezecib
Link to comment
Share on other sites

But so far I'm really loving modding in Don't Starve, everything has been really easy to figure out, and everybody on the forum is being so helpful as well.
I think the same ! This modding community is great =)

 

It seems everytime I get help from you rezecib, I learn something about lua and the mod tools that I never realized.
True !

Every time I post some of my code, @rezecib appear and tells me how to improve it. And I am just like "Oh ? You can DO this ? Woooooohh !"
It's like the Djinni of DST modding =P

Link to comment
Share on other sites

Aha, to think I wouldn't have realized thats why spawn prefab didn't work, now I know (insert GI-JOE reference)

 

 

 That's correct, I messed that up.

It's still got another problem though.  inst:ListenForEvent("entity_death"... never triggers.

 

It works when I replace it with TheWorld:ListenForEvent("entity_death"... but then it doesn't have a reference to the inst that SHOULD be listening for it.

 

I can make a workaround that tries to find it manually, but I'm sure theres a better way (as you have proven very thoroughly).

Link to comment
Share on other sites

It works when I replace it with TheWorld:ListenForEvent("entity_death"... but then it doesn't have a reference to the inst that SHOULD be listening for it.

 

Try inst:ListenForEvent("entity_death", <function>, TheWorld). You will still get the reference to inst, and the source will be TheWorld, so it should trigger.

 

Nope.

Edited by Jjmarco
Link to comment
Share on other sites

Try inst:ListenForEvent("entity_death", <function>, TheWorld). You will still get the reference to inst, and the source will be TheWorld, so it should trigger.

 

Well it's triggering now, but the inst value is being set as "forest" instead of my character.

 

Edit: Is there a way I can have TheWorld and my character as the source at the same time?  I didn't know listenforevent could change the source, what else can I pop into that arguement?

 

Edit 2: As it turns out, by changing the source to TheWorld, TheWorld replaces my character as the inst.

 

Edit 3: Hey I fixed it, I just changed 

inst:ListenForEvent("entity_death", function(inst, data)

to 

inst:ListenForEvent("entity_death", function(world, data)

And now inst remains as the original character.

 

 

That was cool, I didn't know you could change the source of ListenForEvent while keeping values like that

Edited by Zackreaver
Link to comment
Share on other sites

@Zackreaver, Oh, really?

Well, try this then: in the function called by the event, data also contains afflicter, the one who killed the entity that triggered the event. So you could check if afflicter is your character and act accordingly.

 

Nevermind, this works too!

Edited by Jjmarco
Link to comment
Share on other sites

@Jjmarco, I think I fixed it while you were replying, but I got a new problem

 

With AddPrefabPostInit("rabbit", function(inst)

I don't think TheWorld exists yet, so I'm pretty sure it doesn't know where to get the source from.

 

Regardless, the ListenForEvent on the AddPrefabPostInit("rabbit", isn't triggering

 
Edit: Yeah, TheWorld is a nil value at the time this function is taking place, so the same trick won't work here.
 
Edit 2: But GLOBAL.TheWorld exists!!  I fixed it
Edited by Zackreaver
Link to comment
Share on other sites

That was cool, I didn't know you could change the source of ListenForEvent while keeping values like that

 

Well, the thing is that now that you changed inst to world in the function's arguments, inst isn't being overwritten anymore, and will still refer to your character.

 

 

Edit 2: But GLOBAL.TheWorld exists!!  I fixed it

 

Yeah, don't forget, you don't have direct access to global variables in your modmain, so always stick a GLOBAL before any of them!

Link to comment
Share on other sites

Hmm, that's weird that it wasn't triggering... This is the (working) code I have in Wigfrid's master_postinit:

    inst:ListenForEvent("entity_death", function(wrld, data) onkill(inst, data) end, TheWorld)

So you could do the same thing there, pass TheWorld (or GLOBAL.TheWorld) to a wrapper function that ignores it and passes it inst. I don't see why setting it up that way would cause it to hear the event when it otherwise wouldn't... But in both cases you definitely need to be passing the proper inst, so this setup would ensure that.

Link to comment
Share on other sites

@rezecib, Okay, got it working for the most part.

 

I have three different questions

 

First I have a performance related question.

 

There could be a possibility of 6 (or more) of this character in a single multiplayer game at once, and they could have anywhere from 1 to the inventory capacity of rabbits in their inventory at a time.

 

Will these functions create lag as a result?  do the timers running in the background create any heavy memory usage?  If I decide to shorten the timers for a particular reason how taxing on systems could it possibly get?

 

Is there anyway I can measure the amount of memory being used by these functions in the event of a performance hit?

 

I'm thinking ahead on this particular feature and the thought of 100 or more rabbit's generating gifts at the same time creating a bit of a lag spike.

 

Second, whats a good way of converting the list of gifts to a weighted tabled

 

For example, I'm thinking of having some items be obtained as gifts more often than others, making certain other gifts rarer than others.

 

The immediate way I can think of, is just putting duplicates of the same item to make it more common, which is what I'm gonna work with at the moment since it requires the least amount of work and can be used immediately.  But I'm not sure if there might be a different way to adjust the weight, especially if the gift pool ends up being big.  But in the meantime, this method seems like the easiest way.

 

Third, I want to make Rabbit's not run away and Bunnymen not care if this character holds meat

 

I read in your guide that I should avoid editing other files unless absolutely necessary.  But I'm sorta stumped on how to add these character specific features in.  

 

I know from your explanation that I should do something like storing the old function of 

local function NormalRetargetFn(inst)

from the bunnyman prefab into a variable then passing my function which has a filter to check if the return value's prefab of the original function is not my characters.  But I'm not exactly sure how to write that out so that it works if I'm even on the right track.

Edited by Zackreaver
Link to comment
Share on other sites

@Zackreaver, I believe this is the most efficient way of implementing it that doesn't involve powerful sorcery. You can look at scheduler.lua to see how it's implemented. I don't think 100 rabbits with scheduled task should be a problem, especially when you consider most lag occurs when there are lots of brains, which are all doing range checks (a very expensive operation) every sim tick.

 

Weighted table... you could look at the lootdropper component, for example spiders use this to have a weighted table.

 

You can get at the NormalRetargetFn through the bunnyman's combat component, which is what it gets attached to. For bunnies, you can remove the "scarytoprey" tag, although this may also affect gobblers and maybe something else (birds I know check directly for the "player" tag).

Edited by rezecib
Link to comment
Share on other sites

@rezecib, I managed to get the weighted table I was looking for for the most part, still fiddling with methods to get the rabbits and bunnymen to act properly.

 

 

In the mean time I wanted to see if I can tackle a separate task.

 

I edited my main post as well but I want to see if I can remove the ability for this character to attack and murder rabbits.  I understand it involves adjustment of actions which have given me trouble in the past.

Link to comment
Share on other sites

@Zackreaver, Actually, it's easier than that. DarkXero posted a solution here. Although removing the murdering part may require adjusting the component action... actually, looking at it, it calls the replica.health:CanMurder function, so you can populate that for rabbits and check the inventoryitem component's owner to get the player prefab.

Link to comment
Share on other sites

next = GLOBAL.nextlocal untouchables = {    rabbit = true} AddComponentPostInit("playeractionpicker", function(self)    local old = self.SortActionList    function self:SortActionList(actions, target, useitem)		if self.inst.prefab == "wilson" then			local i = next(actions, nil)			while i do			   if actions[i] == GLOBAL.ACTIONS.ATTACK and untouchables[target.prefab] or			   actions[i] == GLOBAL.ACTIONS.MURDER and untouchables[useitem.prefab] or			   actions[i] == GLOBAL.ACTIONS.COOK and untouchables[useitem.prefab] then			      table.remove(actions, i)				  i = nil			   else			      i = next(actions, i)			   end			end			return old(self, actions, target, useitem)		else			return old(self, actions, target, useitem)		end    endend)

Change Wilson to your character's prefab:

1) You can't attack rabbits now

2) You can't murder rabbits now

3) You can't kill rabbits by cooking them in a campfire

Link to comment
Share on other sites

@DarkXero,Incredible, I thought that as gonna be alot more work than it actually was.

 

But I might still need some action help here.

 

In it's place I want to have instead of an attack action, to have a "Pet" action, which will cause the rabbit to enter it's stunned state so it can be picked up.

 

I think I might be able to figure it out with the examples I've been given so far, but any extra help is appreciated. 

 

@rezecib, In the event the "scarytoprey" tag causes other creatures to act strange, is there any workaround I can do for this?  Perhaps adding another check to the rabbit's in a clean way so that they only act different around my character?

Edited by Zackreaver
Link to comment
Share on other sites

You can use

inst:RemoveTag("scarytoprey")

in your master_postinit to get close to butterflies and rabbits. Birds react to the player tag also, but you are focusing on rabbits.

 

For the pet action, the pain is not the action itself, but the networking (or at least it used to be :razz: ). Try making the action server side, and then figure out how rpcs work to make client send a custom mod rpc to the server when performing the action. I will see what I can do.

Link to comment
Share on other sites

You can use ? 1 inst:RemoveTag("scarytoprey")

 

 

While that works, it also causes the gobblers to become passive as well.  I don't want that, I only want the rabbit's to be passive.

 

Isn't there some way I can make the rabbits brain check to see if it's my character before they run away?  I know that modifying the rabbit file directly would cause conflicts with other mods, but how would I make the change cleanly?

 

Edit: If that request sounds too extreme, as a workaround is there a way I can make the "scarytoprey" tag only appear on the character when gobblers are around?  How hard is it to scan the proximity of the character before adding a tag?

 

I'm trying to look for the cleanest solution to get this result.

Edited by Zackreaver
Link to comment
Share on other sites

AddBrainPostInit("rabbitbrain", function(self)	local check = function(hunter)		return not hunter.prefab == "wilson"	end	self.bt.root.children[3].shouldrunfn = check	self.bt.root.children[4].shouldrunfn = checkend)

in modmain.lua. Change wilson for your character's prefab.

Link to comment
Share on other sites

@DarkXero, New request, can I borrow your brain?  Because so far it's alot more amazing than mine.

 

I cannot even fathom what you did, let alone how clean and small it is.

 

One thing is for certain, these are incredibly helpful examples for me to reference on.

 

Infinity thanks.

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