Jump to content

Custom Stat as Crafting Material


Recommended Posts

As title stats, I'm finding this to be pretty much impossible to figure out..

did this to add the stat and image

GLOBAL.CHARACTER_INGREDIENT["CUSTOMSTAT"] = "decrease_customstat"

but sadly still gives the following error:

WARNING! Could not find region 'FROMNUM' from atlas 'FROMNUM'. Is the region specified in the atlas?
Looking for default texture '' from atlas 'FROMNUM'.
images/inventoryimages.xml

Usually I just add the atlasname but I'm not sure how I would go about doing this for stats.

Now while I had this issue, I decided to explore where the default stats are being used, and in builder_replica I found out that it has an if for every stat, which I think, means that it doesn't support custom stats, unless I edit base files?

Link to comment
Share on other sites

This is very easy to set up, you have to overload a few function in order to do it though. Here is a simple example you will have to modify it as needed for your own usage.

EXAMPLE:

Texture/Atlas Files:

E4gT3X1.png

modmain.lua code:

CHARACTER_INGREDIENT["LEVEL"] = "decrease_level"

local recipe = AddRecipe( "soul_essence", { Ingredient( CHARACTER_INGREDIENT.LEVEL, 5, "images/decrease_level.xml") }, RECIPETABS.MAGIC, TECH.NONE, nil, nil, nil, nil, "wortox", "images/inventoryimages/soul_essence.xml", "soul_essence.tex" )


local builder_replica = GLOBAL.require("components/builder_replica")

local function BuilderReplicaPostInit( inst )

	local _RemoveIngredients = inst.RemoveIngredients
		function inst:RemoveIngredients( ingredients, recname )

			local recipe = AllRecipes[recname]

			if recipe then
				for k,v in pairs(recipe.character_ingredients) do
					if v.type == CHARACTER_INGREDIENT.LEVEL then
						self.inst.replica.leveler:SetLevel( TUNING.WORTOX.LEVEL_SYSTEM, 							self.inst.components.leveler:GetLevel( TUNING.WORTOX.LEVEL_SYSTEM ) - v.amount )
					end
				end
			end

			_RemoveIngredients( self, ingredients, recname )
		end

		local _HasCharacterIngredient = inst.HasCharacterIngredient
		function inst:HasCharacterIngredient( ingredient )
			local _ret = _HasCharacterIngredient( self, ingredient )		
			if not _ret then
				if ingredient.type == CHARACTER_INGREDIENT.LEVEL then
					if ingredient.level_tag ~= nil and self.inst.replica.leveler ~= nil then
						print( ingredient.level_tag )
						return self.inst.replica.leveler:GetLevel( TUNING.WORTOX.LEVEL_SYSTEM ) >= ingredient.amount
					end
				end
			end

			return _ret		
		end
end

BuilderReplicaPostInit( builder_replica )

Note: Take notice how I pushed an atlas file as the 3rd parameter of the Ingredient class. The atlas file and texture file must be named the same as you put in the custom character ingredient.

character prefab.lua master_postinit function:

local _RemoveIngredients = inst.components.builder.RemoveIngredients
	function inst.components.builder:RemoveIngredients( ingredients, recname )

		local recipe = AllRecipes[recname]

		if recipe then
			for k,v in pairs(recipe.character_ingredients) do
				if v.type == CHARACTER_INGREDIENT.LEVEL then
					self.inst.components.leveler:SetLevel( TUNING.WORTOX.LEVEL_SYSTEM, self.inst.components.leveler:GetLevel( TUNING.WORTOX.LEVEL_SYSTEM ) - v.amount )
				end
			end
		end

		_RemoveIngredients( self, ingredients, recname )
	end

	local _HasCharacterIngredient = inst.components.builder.HasCharacterIngredient
	function inst.components.builder:HasCharacterIngredient( ingredient )
		local _ret = _HasCharacterIngredient( self, ingredient )		
		if not _ret then
			if ingredient.type == CHARACTER_INGREDIENT.LEVEL then
				if self.inst.components.leveler ~= nil then
					return self.inst.components.leveler:GetLevel( TUNING.WORTOX.LEVEL_SYSTEM ) >= ingredient.amount
				end
			end
		end

		return _ret		
	end

Note: Modify the cost above as needed and put it at the very bottom of your master_postinit function, this only runs on the server.

Conclusion:

This can easily be modified to work with any custom stat, you should make a new icon for the stat unlike I, which was used the essence texture as the decrease_level texture file.

Not ENOUGH Ingredients

jTA7gZd.jpg

GOT INGREDIENTS

pRL0BGw.png

 

Edited by Kzisor
Updated the code to work with client side.
Link to comment
Share on other sites

Awesome, didn't know you can put the ingredient atlas as the 3rd parameter, since recipes that required my modded items were causing issue this fixes it too.

Since the stat I made is a stat I give to all characters, I had to figure out where to put that code, which was in the same function in modmain that adds the customstat component to every character.

I did have to change self.inst to inst in the  functions but other than that so far its working perfectly, all I gotta do now is test it to make sure it works with other people online as I haven't tested anything yet.

 

Thanks for your help ^^

Link to comment
Share on other sites

Ok so I got my friend to try it out and he couldn't even join without crashing

I was using this to give the custom stat and its also where i put this ingredient thing:

for k,prefabname in ipairs(GLOBAL.DST_CHARACTERLIST) do
    AddPrefabPostInit(prefabname, AddStat)
end

the stat and character ingriedient were in AddStat but when ever he tries to join he gets this error:

error calling PrefabPostInit: webber in mod workshop-637153586 (Mod):
[string "../mods/workshop-637153586/modmain.lua"]:152: attempt to index field 'builder' (a nil value)
LUA ERROR stack traceback:
        ../mods/workshop-637153586/modmain.lua(152,1)
        =(tail call) ?
        =[C] in function 'xpcall'
        scripts/mods.lua(123,1) in function 'mod'
        scripts/mainfunctions.lua(157,1)

line 152 is this:

local _RemoveIngredients = inst.components.builder.RemoveIngredients

 

So to fix this, I added

if GLOBAL.TheWorld.ismastersim

to your code, but then later realised that this makes the recipes only work for the host

 

So how am I to go about making these builder functions available to everyone?

 

Edited by Aquaterion
Link to comment
Share on other sites

8 hours ago, Aquaterion said:

So how am I to go about making these builder functions available to everyone?

Well, I specifically wrote how to handle it from a custom character perspective because most mods do not add new stats to every character. In order to add a new custom character ingredient for all characters you must use the components code for the server and then use replica code for the client side (note: you simply replace components with replica and it should work for clients, but you must have both sets of code for it to work across the board).

Edited by Kzisor
Removed code as it did not work as intended.
Link to comment
Share on other sites

Damn I thought it was gonna be that easy.. :(

error calling PrefabPostInit: wilson in mod workshop-637153586 (Mod): 
[string "../mods/workshop-637153586/modmain.lua"]:153: attempt to index field 'builder' (a nil value)
LUA ERROR stack traceback:
        ../mods/workshop-637153586/modmain.lua(153,1)
        =(tail call) ?
        =[C] in function 'xpcall'
        scripts/mods.lua(123,1) in function 'mod'
        scripts/mainfunctions.lua(157,1)

 

Same error I was having at the start, the 1 I had to do ismastersim to fix

---

for k,prefabname in ipairs(GLOBAL.DST_CHARACTERLIST) do
    AddPrefabPostInit(prefabname, AddStat)
end

is this even a good way to put the code on all characters?

Edited by Aquaterion
Link to comment
Share on other sites

5 hours ago, Aquaterion said:

is this even a good way to put the code on all characters?

To answer this question, no that is a bad way of handling that. You should use 'AddPlayerPostInit' it accepts 1 argument which is a function.

To answer how to make this work with clients, use the following code and alter it as needed.


local builder_replica = GLOBAL.require("components/builder_replica")

local function BuilderReplicaPostInit( inst )

	local _RemoveIngredients = inst.RemoveIngredients
		function inst:RemoveIngredients( ingredients, recname )

			local recipe = AllRecipes[recname]

			if recipe then
				for k,v in pairs(recipe.character_ingredients) do
					if v.type == CHARACTER_INGREDIENT.LEVEL then
						self.inst.replica.leveler:SetLevel( TUNING.WORTOX.LEVEL_SYSTEM, 							self.inst.components.leveler:GetLevel( TUNING.WORTOX.LEVEL_SYSTEM ) - v.amount )
					end
				end
			end

			_RemoveIngredients( self, ingredients, recname )
		end

		local _HasCharacterIngredient = inst.HasCharacterIngredient
		function inst:HasCharacterIngredient( ingredient )
			local _ret = _HasCharacterIngredient( self, ingredient )		
			if not _ret then
				if ingredient.type == CHARACTER_INGREDIENT.LEVEL then
					if ingredient.level_tag ~= nil and self.inst.replica.leveler ~= nil then
						print( ingredient.level_tag )
						return self.inst.replica.leveler:GetLevel( TUNING.WORTOX.LEVEL_SYSTEM ) >= ingredient.amount
					end
				end
			end

			return _ret		
		end
end

BuilderReplicaPostInit( builder_replica )

 

Edit:

I've updated the original code I posted so if you are viewing this post after 03/06/2016 use the code in the original post I made as it will work perfectly.

Edited by Kzisor
Added a tidbit of information about an update on the original post I made.
Link to comment
Share on other sites

Just now, Aquaterion said:

Do I remove everything you told me to put before or just the one with the replicas?I can't test it right now as I'm bout to go to bed. Thanks for the help so far ^^

Delete everything you've added thus far and use the code from the original post I made.

Link to comment
Share on other sites

23 hours ago, Kzisor said:

Delete everything you've added thus far and use the code from the original post I made.

I haven't been able to try it yet as the people I was testing with are both offline, but just to be clear, first put the new "replica" code, then the old "component" code right?

Edited by Aquaterion
Link to comment
Share on other sites

Just tested it out, still other clients can't use the stat as a recipe, btw, their stat meters are also not working(this was happening before, i just wanted to point it out), but the stat should be working.

If you want I can send you the files, maybe i've done something wrong

Link to comment
Share on other sites

12 minutes ago, Aquaterion said:

Just tested it out, still other clients can't use the stat as a recipe, btw, their stat meters are also not working(this was happening before, i just wanted to point it out), but the stat should be working.

If you want I can send you the files, maybe i've done something wrong

If it's not working on your end then you've did something in correct and you will need to debug it on your end. The code supplied works effectively across both my computers.

Link to comment
Share on other sites

54 minutes ago, Kzisor said:

If it's not working on your end then you've did something in correct and you will need to debug it on your end. The code supplied works effectively across both my computers.

Hmm Maybe the issue is with using self.inst or inst, at first changing self.inst to inst helped, atleast in the host one, but this 1, didn't:

 

Spoiler

 


[string "../mods/workshop-637153586/modmain.lua"]:197: attempt to index field 'replica' (a nil value)
LUA ERROR stack traceback:
../mods/workshop-637153586/modmain.lua:197 in (upvalue) _HasCharacterIngredient (Lua) <193-204>
   self =
      classified = 100745 - player_classified (valid:true)
      ondetachclassified = function - scripts/components/builder_replica.lua:28
      inst = 100037 - wilson (valid:true)
   ingredient = table: 238DACC8
   _ret = false
../mods/workshop-637153586/modmain.lua:194 in (method) HasCharacterIngredient (Lua) <193-204>
   self =
      classified = 100745 - player_classified (valid:true)
      ondetachclassified = function - scripts/components/builder_replica.lua:28
      inst = 100037 - wilson (valid:true)
   ingredient = table: 238DACC8
scripts/components/builder_replica.lua:228 in (method) CanBuild (Lua) <212-236>
   self =
      classified = 100745 - player_classified (valid:true)
      ondetachclassified = function - scripts/components/builder_replica.lua:28
      inst = 100037 - wilson (valid:true)
   recipename = rasengan
   recipe = table: 238DB768
   i = 1

 

 

And this is what I have in modmain.lua

 

Spoiler

 

local function GiveStat(inst)
    if inst.components.stat == nil then
        inst:AddComponent("stat")
        inst.components.stat:StartCharge(1, 3)
        AddClassPostConstruct("widgets/controls", AddStatIndicator)
            
        local builder_replica = GLOBAL.require("components/builder_replica")

        local function BuilderReplicaPostInit( inst )

            local _RemoveIngredients = inst.RemoveIngredients
                function inst:RemoveIngredients( ingredients, recname )

                    local recipe = AllRecipes[recname]

                    if recipe then
                        for k,v in pairs(recipe.character_ingredients) do
                            if v.type == CHAR_ING.STAT then
                                inst.components.stat:UseAmount(v.amount)
                            end
                        end
                    end

                    _RemoveIngredients( self, ingredients, recname )
                end

                local _HasCharacterIngredient = inst.HasCharacterIngredient
                function inst:HasCharacterIngredient( ingredient )
                    local _ret = _HasCharacterIngredient( self, ingredient )        
                    if not _ret then
                        if ingredient.type == CHAR_ING.STAT then
                            if inst.replica.stat~= nil then
                                return inst.replica.stat:GetCurrent() >= ingredient.amount
                            end
                        end
                    end

                    return _ret        
                end
        end
        
        BuilderReplicaPostInit( builder_replica )

 

 

Maybe I'm doing the stat_replica.lua wrong? Honestly I didn't have 1 before you mentioned it, as I didn't know what its used for.

 

Edited by Aquaterion
Link to comment
Share on other sites

47 minutes ago, Aquaterion said:

Maybe I'm doing the stat_replica.lua wrong? Honestly I didn't have 1 before you mentioned it, as I didn't know what its used for.

Replica's are kinda like components, but run on the clients. Are you using AddReplicaComponent to actually add the component replica?

Example:

AddReplicableComponent("leveler")
Link to comment
Share on other sites

Just now, Kzisor said:

Replica's are kinda like components, but run on the clients. Are you using AddReplicaComponent to actually add the component replica?

Example:


AddReplicableComponent("leveler")

where do i do that, cuz I surely didn't.. is that like "AddComponent("leveler") or?

Edited by Aquaterion
Link to comment
Share on other sites

YES I GOT IT TO WORK, THANKS SO MUCH, NOW ALL I GOTTA FIX IS THE METER!! The problem is that it doesn't show the current number for clients. I tried replacing some things with replica but I don't believe it worked, any ideas?

 

 

Spoiler

 

local Badge = require "widgets/badge"
local UIAnim = require "widgets/uianim"
local Text = require "widgets/text"

local StatBadge = Class(Badge, function(self, owner)
    self.owner = ThePlayer
    Badge._ctor(self, "stat", self.owner)
    
    self.statarrow = self.underNumber:AddChild(UIAnim())
    self.statarrow:GetAnimState():SetBank("sanity_arrow")
    self.statarrow:GetAnimState():SetBuild("sanity_arrow")
    self.statarrow:GetAnimState():PlayAnimation("neutral")
    self.statarrow:SetClickable(false)
    
    if self.owner.components.stat then
        self.onstatdelta = function(owner, data) self:StatDelta(data) end
        self.owner:ListenForEvent("statdelta", self.onstatdelta, self.owner)

        self:SetStatPercent(self.owner.components.stat:GetPercent())
    end

    self.num = self:AddChild(Text(BODYTEXTFONT, 33))
    self.num:SetHAlign(ANCHOR_MIDDLE)
    self.num:SetPosition(3.5, 1, 0)
    self.num:Hide()
    
    self:StartUpdating()
end)

function StatBadge:StatDelta(data)
    self:SetStatPercent(data.newpercent)
end

function StatBadge:SetStatPercent(pct)
    Badge.SetPercent(self, pct, self.owner.components.stat.max)

  
    self.num:SetString(tostring(math.ceil(pct * self.owner.components.stat.max)))
end

function StatBadge:OnUpdate(dt)
    if self.owner:HasTag('playerghost') then
        self:Hide()
    else
        self:Show()
    end
    
    local up = self.owner ~= nil and
        self.owner.components.stat ~= nil and
        self.owner.components.stat:GetPercent() < 1

    local anim = up and "arrow_loop_increase" or "neutral"
    if self.arrowdir ~= anim then
        self.arrowdir = anim
        self.statarrow:SetPosition(0, -4, 0)
        self.statarrow:GetAnimState():PlayAnimation(anim, true)
    end
end

return StatBadge

 

 

Edited by Aquaterion
Link to comment
Share on other sites

10 hours ago, Aquaterion said:

YES I GOT IT TO WORK, THANKS SO MUCH, NOW ALL I GOTTA FIX IS THE METER!! The problem is that it doesn't show the current number for clients. I tried replacing some things with replica but I don't believe it worked, any ideas?

You have to use net variables for widgets/replicas. You can learn about net variables in one of the stickied posts by @PeterA.

Link to comment
Share on other sites

11 hours ago, Kzisor said:

You have to use net variables for widgets/replicas. You can learn about net variables in one of the stickied posts by @PeterA.

function Stat:GetPercent()
    if self.inst.components.stat ~= nil then
        return self.inst.components.stat:GetPercent()
    elseif self.classified ~= nil then
        return self.inst["current"]:value() / self.inst["max"]:value()
    else
        return 1
    end
end

 

doing print(c_sel().replica.stat:GetPercent()) works in console when hosting a local dedicated server, so im not sure why the badge aint using it even tho I clearly am calling them:

local Badge = require "widgets/badge"
local UIAnim = require "widgets/uianim"
local Text = require "widgets/text"

local StatBadge= Class(Badge, function(self, owner)
    Badge._ctor(self, "stat", owner)

    self.statarrow = self.underNumber:AddChild(UIAnim())
    self.statarrow:GetAnimState():SetBank("sanity_arrow")
    self.statarrow:GetAnimState():SetBuild("sanity_arrow")
    self.statarrow:GetAnimState():PlayAnimation("neutral")
    self.statarrow:SetClickable(false)
    
    
    self.num = self:AddChild(Text(BODYTEXTFONT, 33))
    self.num:SetHAlign(ANCHOR_MIDDLE)
    self.num:SetPosition(3.5, 1, 0)
    self.num:Hide()
    
    self:StartUpdating()
    
    if self.owner.replica.statthen
        self.onstatdelta = function(owner, data) self:StatDelta(data) end
        self.owner:ListenForEvent("statdelta", self.onstatdelta, self.owner)

        self:SetStatPercent(self.owner.replica.stat:GetPercent())
    end

    
end)

    function StatBadge:StatDelta(data)
        self:SetStatPercent(data.newpercent)
    end

    function StatBadge:SetStatPercent(pct, current, max)
        current = current or 50
        max = max or 100
        pct = pct or (current / max)
        Badge.SetPercent(self, pct, max)    
        
        self.anim:GetAnimState():SetPercent("anim", 1 - pct)
        
        if pct <= .2 then
            self:StartWarning()
        else
            self:StopWarning()
        end
        
        if pct > .2 and pct < 1 then
            self:StartWarning(0, 80/255, 1, 1)
        else
            self:StopWarning()
        end
        
        self.num:SetString(tostring(math.ceil(pct * max)))
    
    end

    function StatBadge:OnUpdate(dt)
        if self.owner:HasTag('playerghost') then
            self:Hide()
        else
            self:Show()
        end
        
        local up = self.owner ~= nil and
            self.owner.replica.stat ~= nil and
            self.owner.replica.stat:GetPercent() < 1

        local anim = up and "arrow_loop_increase" or "neutral"
        if self.arrowdir ~= anim then
            self.arrowdir = anim
            self.statarrow:SetPosition(0, -4, 0)
            self.statarrow:GetAnimState():PlayAnimation(anim, true)
        end
        
        if self.owner.replica.stat ~= nil then
            self:SetStatPercent(
            self.owner.replica.stat:GetPercent(),
            self.owner.replica.stat:GetCurrent(),
            self.owner.replica.stat:Max())
        end
    end

return StatBadge

Edited by Aquaterion
Link to comment
Share on other sites

22 hours ago, Kzisor said:

Take a look at the following topic's attachments, I posted.

 

Ok so like after 6 hours of changing things, I managed to get the netvar thing to not crash the game.. I went to a local dedicated server to try it... andd badge still doesn't change.. Weird thing is, I made the console print the value when it changes, and it does print the value changing but the client side always shows 100/100

local function Set( netvar, value )
    print("Set:value:" .. value) -- *
    netvar:set(value)
end

--* This prints correctly on server console, and incorrectly on client console, always printing 100

function Stat:SetCurrent(current)
    Set(self.inst["current"], current)
end

I really don't wanna give up as I did implement a good amount of features into this mod.. I don't get why a damn number is so hard to display for clients..

It seems client --> server works, but server --> client doesn't, from what I understood

I think its because I don't explain how statdirty works exactly..

---

Edit: So I changed some things and now my stat meter is blinking, and when i hover on it it shows -1.IND

It's blinking depending on the DoDelta, so progress or what?

Edited by Aquaterion
Link to comment
Share on other sites

11 hours ago, Aquaterion said:

Ok so like after 6 hours of changing things, I managed to get the netvar thing to not crash the game.. I went to a local dedicated server to try it... andd badge still doesn't change.. Weird thing is, I made the console print the value when it changes, and it does print the value changing but the client side always shows 100/100

local function Set( netvar, value )
    print("Set:value:" .. value) -- *
    netvar:set(value)
end

--* This prints correctly on server console, and incorrectly on client console, always printing 100

function Stat:SetCurrent(current)
    Set(self.inst["current"], current)
end

I really don't wanna give up as I did implement a good amount of features into this mod.. I don't get why a damn number is so hard to display for clients..

It seems client --> server works, but server --> client doesn't, from what I understood

I think its because I don't explain how statdirty works exactly..

---

Edit: So I changed some things and now my stat meter is blinking, and when i hover on it it shows -1.IND

It's blinking depending on the DoDelta, so progress or what?

Make sure you're listening for the event which is suppose to be sent across the network (the "netvardirty" when you create the net variable) on the client also make sure the net variable is on the client not only on the server.

Link to comment
Share on other sites

14 minutes ago, Kzisor said:

Make sure you're listening for the event which is suppose to be sent across the network (the "netvardirty" when you create the net variable) on the client also make sure the net variable is on the client not only on the server.

Hmm actually, I think the "current" value is working fine, but "max" isn't.. not sure why..

nevermind.. still not working

I do have this in modmain.lua in the PlayerPostInit:

if not TheWorld.ismastersim then
            inst:ListenForEvent("currentdirty", OnStatDirty)
end

I THINK ITS FINALLY WORKING OMG

Edited by Aquaterion
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...