SenL Posted January 26, 2016 Share Posted January 26, 2016 (edited) Hi, Does anyone know/have an example or template of character mod with level system (level up by killing, eat, etc)? (I mean one with really good coding practice) Thanks. Edited January 26, 2016 by SenL Link to comment Share on other sites More sharing options...
Kzisor Posted January 27, 2016 Share Posted January 27, 2016 (edited) I use the attached components as my leveling systems in all my characters. It's a generic leveling system which allows you to level up based on whatever criteria you want to do it by. Example Usage: Tuning Settings: LEVEL_SYSTEM = "[INSERT NAME HERE]" START_EXPERIENCE = 10 -- The amount of "experience" you need to level. EXPERIENCE_MULT_MAX = 0.75 -- The multiplier in which will be used to make leveling easier/more difficult. MAX_LEVEL = 1000 -- Any number really. -- Experience on doing stuff. FOOD_EXPERIENCE = 10 KILL_EXPERIENCE = 10 -- Stats MIN/MAX MIN_HEALTH = 100 MAX_HEALTH = 400 MIN_HUNGER = 100 MAX_HUNGER = 600 MIN_SANITY = 100 MAX_SANITY = 200 MIN_DAMAGE_MULT = 1 MAX_DAMAGE_MULT = 5 MIN_SPEED_MULT = 1 MAX_SPEED_MULT = 3 Player Prefab: local function determinestats( inst ) -- Determine the new stat increases below. -- Determines the new max hunger. inst.components.hunger.max = math.ceil( MIN_HUNGER + inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * (MAX_HUNGER - MIN_HUNGER) / MAX_LEVEL ) -- Determins the new max health. inst.components.health.maxhealth = math.ceil( MIN_HEALTH + inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * (MAX_HEALTH - MIN_HEALTH) / MAX_LEVEL ) -- Determines the new max sanity. inst.components.sanity.max = math.ceil( MIN_SANITY + inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * (MAX_SANITY - MIN_SANITY) / MAX_LEVEL ) -- Determines the new combat modifier. inst.components.combat.damagemultiplier = ( MIN_DAMAGE_MULT + ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * ( MAX_DAMAGE_MULT - MIN_DAMAGE_MULT ) / MAX_LEVEL ) ) -- Determines the new run speed. local speed = ( MIN_SPEED_MULT + ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * ( MAX_SPEED_MULT - MIN_SPEED_MULT ) / MAX_LEVEL ) ) inst.components.locomotor:SetExternalSpeedMultiplier( inst, "Acceleration", speed ) -- End new stat stuff. -- Makes the badges flash. inst.components.hunger:DoDelta(0) inst.components.health:DoDelta(0) inst.components.sanity:DoDelta(0) end local function onlevelup( inst, data ) if data.system == LEVEL_SYSTEM then if inst.components.leveler:GetLevel( LEVEL_SYSTEM ) < MAX_LEVEL then inst.components.leveler:SetLevel( LEVEL_SYSTEM, ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) + 1 ) ) local max_exp = ( ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * START_EXPERIENCE ) * EXPERIENCE_MULT_MAX ) inst.components.leveler:SetExperience( LEVEL_SYSTEM, data.experience ) -- Needs to be adjusted based on current experience over current level. inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, max_exp ) -- Sets the max experience needed for the next level. determinestats( inst ) -- Will determine the stat increases based on leveling. -- Sets the new max values here, allows you to use determine stats in other places without setting max health every time. inst.components.health:DoDelta( inst.components.health.maxhealth ) inst.components.hunger:DoDelta( inst.components.hunger.max ) inst.components.sanity:DoDelta( inst.components.sanity.max ) end end end local function onleveldelta( inst, data ) if data.system == LEVEL_SYSTEM then inst.components.leveler:SetExperience( LEVEL_SYSTEM, data.experience) end end local function reset_leveler( inst ) -- Resets the leveler component with the system to 0. inst.components.leveler:SetLevel( LEVEL_SYSTEM, 0 ) inst.components.leveler:SetExperience( LEVEL_SYSTEM, 0 ) inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, START_EXPERIENCE ) end local function ondeath( inst ) reset_leveler() end local function oneat( inst, food ) inst.components.leveler:DoDelta( LEVEL_SYSTEM, FOOD_EXPERIENCE ) end local function onkilled( inst, data ) inst.components.leveler:DoDelta( LEVEL_SYSTEM , KILL_EXPERIENCE ) end local function OnExperienceDirty( inst, system ) if inst ~= nil then local experience = inst[system.."_currentexperience"]:value() local maxexperience = inst[system.."_maxexperience"]:value() local level = inst[system.."_currentlevel"]:value() local maxlevel = inst[system.."_maxlevel"]:value() inst:PushEvent( "experiencedirty", { newpercent = experience / maxexperience, maxexperience = maxexperience, currentlevel = level, maxlevel = maxlevel } ) end end local function AddLevelSystem( inst, system ) inst[system.."_currentexperience"] = net_ushortint( inst.GUID, "leveler.SYSTEMS["..system.."].currentexperience", system.."_currentexperiencedirty") inst[system.."_maxexperience"] = net_ushortint( inst.GUID, "leveler.SYSTEMS["..system.."].maxexperience", system.."_currentexperiencedirty") inst[system.."_currentlevel"] = net_ushortint( inst.GUID, "leveler.SYSTEMS["..system.."].currentlevel", system.."_currentexperiencedirty") inst[system.."_maxlevel"] = net_ushortint( inst.GUID, "leveler.SYSTEMS["..system.."].maxlevel", system.."_currentexperiencedirty") inst:ListenForEvent(system.."_currentexperiencedirty", function( inst ) OnExperienceDirty(inst, system ) end) end local function onload( inst, data ) -- NEED TO DETERMINE THE STATS ON LOAD! determinestats( inst ) --re-set these from the save data, because of load-order clipping issues if data.health and data.health.health then inst.components.health:SetCurrentHealth(data.health.health) end if data.hunger and data.hunger.hunger then inst.components.hunger.current = data.hunger.hunger end if data.sanity and data.sanity.current then inst.components.sanity.current = data.sanity.current end inst.components.health:DoDelta(0) inst.components.hunger:DoDelta(0) inst.components.sanity:DoDelta(0) end local function commonpostinit(inst) AddLeveLSystem( inst, LEVEL_SYSTEM ) -- In commonpostinit so we get the value on the client as well as the server, mainly used for badges/widgets. end local function masterpostinit(inst) -- Because other things may use this component we ensure only to add it to our character once. if not inst.components.leveler then inst:AddComponent( "leveler" ) end -- Component set-up. inst.components.leveler:AddLevelSystem( LEVEL_SYSTEM ) inst.components.leveler:SetMaxLevel( LEVEL_SYSTEM, MAX_LEVEL ) -- Note: Requires the level system because it's a generic component. inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, START_EXPERIENCE) -- Note: Requires the level system because it's a generic component. inst:ListenForEvent( "levelup", onlevelup ) inst:ListenForEvent( "leveldelta" onleveldelta ) inst:ListenForEvent( "death", ondeath ) -- Gain Experience Events/Functions inst:ListenForEvent( "killed", onkilled ) --Used to gain experience on kill. inst.components.eater:SetOnEatFn( oneat ) -- Forces badge updates. inst:DoTaskInTime(1, function( inst ) inst.components.leveler:DoDelta( LEVEL_SYSTEM, 0 ) end) -- Needed to get the correct stat values based on level. inst.OnLoad = onload end modmain.lua AddReplicableComponent("leveler") The amount of code needed just for a "basic" leveling system probably is about 3/4 of this, however, these components allow you to have a ONE-MANY system, ONE character with MANY leveling systems. This is good because you it allows you to have different leveling systems to level different stats if you set it up that way. leveler.lua leveler_replica.lua P.S. Remember to give credit where credit is due if you use these components. Edited January 27, 2016 by Kzisor Link to comment Share on other sites More sharing options...
SenL Posted January 27, 2016 Author Share Posted January 27, 2016 Wow. Link to comment Share on other sites More sharing options...
SenL Posted January 28, 2016 Author Share Posted January 28, 2016 (edited) I have a question on this line: inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, START_EXPERIENCE) Why is the max xp is 10? Edit: Oh ok nevermind, in your system you reset exp to 0 as you level up and you need 10 to level up. So it's not cumulative. Ok. Edited January 28, 2016 by SenL Link to comment Share on other sites More sharing options...
Kzisor Posted January 28, 2016 Share Posted January 28, 2016 15 hours ago, SenL said: I have a question on this line: inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, START_EXPERIENCE) Why is the max xp is 10? Edit: Oh ok nevermind, in your system you reset exp to 0 as you level up and you need 10 to level up. So it's not cumulative. Ok. It can be any number, this was just a simple template of how the components work. You can modify any of the code to work the way you want it to work. Example, in the onlevelup function you should see where it's actually setting the new MaxExperience for that level. local max_exp = ( ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * START_EXPERIENCE ) * EXPERIENCE_MULT_MAX ) inst.components.leveler:SetExperience( LEVEL_SYSTEM, data.experience ) -- Needs to be adjusted based on current experience over current level. inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, max_exp ) -- Sets the max experience needed for the next level. Level 1: 1 * 10 * .75 = 7.5 Level 2: 2 * 10 * .75 = 15 If you want to change how much max experience you need per level you simply adjust the equation for the max_exp variable. Link to comment Share on other sites More sharing options...
SenL Posted January 29, 2016 Author Share Posted January 29, 2016 Got it. So what's the difference between onload and onpreload? What if I only have onpreload (and no onload)? Link to comment Share on other sites More sharing options...
Kzisor Posted January 29, 2016 Share Posted January 29, 2016 (edited) 32 minutes ago, SenL said: What if I only have onpreload (and no onload)? onpreload means that it will be executed before the game is loaded. onload means it will be executed after the game has loaded. You want to use onload function with these components as the game should be loaded when you get the values. Edited January 29, 2016 by Kzisor Elaborated on the answer. Link to comment Share on other sites More sharing options...
SenL Posted January 29, 2016 Author Share Posted January 29, 2016 (edited) Ok. Thanks. Another question. How come there is no onsave? Edit: Nevermind, it's handled in the component (leveler) self:OnSave() Edited January 29, 2016 by SenL Link to comment Share on other sites More sharing options...
Kzisor Posted January 29, 2016 Share Posted January 29, 2016 Just now, SenL said: How come there is no onsave? Character prefabs do not save component information, they only load it. The leveler component has it's own onsave and onload functions which will execute to load and save all the data properly. All you need to do is set up the character prefab based on what the component loaded. Link to comment Share on other sites More sharing options...
SenL Posted January 29, 2016 Author Share Posted January 29, 2016 Nice. More questions :). Who do I give credits to? You? Can you add (or is there a) GetNextLevelExp function? I want the character to say this as they gain experience: "Level 1: 60%" (means I'm level 1 and 60% to next level) Link to comment Share on other sites More sharing options...
Kzisor Posted January 29, 2016 Share Posted January 29, 2016 (edited) 1 hour ago, SenL said: Nice. More questions :). Who do I give credits to? You? Can you add (or is there a) GetNextLevelExp function? I want the character to say this as they gain experience: "Level 1: 60%" (means I'm level 1 and 60% to next level) Yes credits go to me as I created those components. You don't need a separate function to make that happen simply add the following code to the onleveldelta function. Example: local exp = inst.components.leveler:GetExperience( LEVEL_SYSTEM ) local max_exp = inst.components.leveler:GetMaxExperience( LEVEL_SYSTEM ) if max_exp > exp then local percent = (exp / max_exp) * 10 inst.components.talker:Say( "Level: " .. tostring(inst.components.leveler:GetLevel( LEVEL_SYSTEM ) ) .. " " .. tostring( percent ) .. "%") end Edited January 29, 2016 by Kzisor Code fix 2.0 Link to comment Share on other sites More sharing options...
SenL Posted January 29, 2016 Author Share Posted January 29, 2016 (edited) Ok great. Right now I got a crash (only 1 mod active, my character mod). New game. Crashes when choosing this character Spoiler local function determinestats(inst) local currlevel = inst.components.leveler:GetLevel(LEVEL_SYSTEM) --crash here ... leveler.lua:87: attempt to index field '?' (a nil value) What am I missing... Edit: I think I found it. I call a function which calls determinestats() before this inst.components.leveler:AddLevelSystem(LEVEL_SYSTEM) in the master_postinit() Duh Edited January 29, 2016 by SenL Link to comment Share on other sites More sharing options...
Kzisor Posted January 29, 2016 Share Posted January 29, 2016 4 minutes ago, SenL said: Ok great. Right now I got a crash (only 1 mod active, my character mod). New game. Crashes when choosing this character Hidden Content leveler.lua:87: attempt to index field '?' (a nil value) What am I missing... Make sure LEVEL_SYSTEM is not nil; that is about the only reason why it would be giving that error. Link to comment Share on other sites More sharing options...
SenL Posted January 29, 2016 Author Share Posted January 29, 2016 I got it fixed. I had to move these much higher in the master_postinit inst.components.leveler:AddLevelSystem(LEVEL_SYSTEM) inst.components.leveler:SetMaxLevel(LEVEL_SYSTEM, MAX_LEVEL) inst.components.leveler:SetMaxExperience(LEVEL_SYSTEM, START_EXPERIENCE) No more crash. However, I'm not seeing level up. I may need to put debug print outs... Link to comment Share on other sites More sharing options...
Kzisor Posted January 29, 2016 Share Posted January 29, 2016 4 minutes ago, SenL said: No more crash. However, I'm not seeing level up. I may need to put debug print outs... I agree that debug printouts are always important when you're not seeing results you might want to see. Link to comment Share on other sites More sharing options...
SenL Posted January 29, 2016 Author Share Posted January 29, 2016 I got an issue with onkilled(inst, data) I want to give exp based on victim's health and damage (some kind of arbitrary formula), not just 10 for all victims. So I do: local function onkilled(inst, data) if data.inst.components.health ~= nil then --if victim has health component, do stuff ... end end ... --in master_postinit ... inst:ListenForEvent("killed", onkilled) ... However it crashes on line "if data.inst.components.health ~= nil then" data.inst is nil, it says. When I changed it to ... inst:ListenForEvent("entity_death", function(world, data) onkilled(inst, data) end, TheWorld) ... It does not crash. So how do I get the original "killed" listenforevent to identify victim prefab (with health and combat) correctly? Thanks! Link to comment Share on other sites More sharing options...
Kzisor Posted January 29, 2016 Share Posted January 29, 2016 4 minutes ago, SenL said: if data.inst.components.health ~= nil then Should be data.victim.components.health; data.inst is a nil value. Link to comment Share on other sites More sharing options...
SenL Posted January 31, 2016 Author Share Posted January 31, 2016 (edited) Kzisor, what console command (while playing) to set level or give exp? Edit: AllPlayers[x].components.leveler:SetLevel() seems to work. Edited January 31, 2016 by SenL Link to comment Share on other sites More sharing options...
Kzisor Posted February 1, 2016 Share Posted February 1, 2016 2 hours ago, SenL said: Kzisor, what console command (while playing) to set level or give exp? Edit: AllPlayers[x].components.leveler:SetLevel() seems to work. That or ThePlayer.components.leveler:SetLevel( LEVEL_SYSTEM, LEVEL ) if you're the host. Link to comment Share on other sites More sharing options...
SenL Posted February 3, 2016 Author Share Posted February 3, 2016 I found a bug on mine (not yours) because each kill is not set to x (static number). Each kill is variable from victim health * victim damage divided by some arbitrary number. Because of this when I kill a treeguard for example I will get 940% exp. I was hoping it would auto-level until it's < 100%. Now how do I do that... Link to comment Share on other sites More sharing options...
SenL Posted February 3, 2016 Author Share Posted February 3, 2016 Hi Kzisor, I'm trying to give exp when chopping but I can't seem to figure it out. It crashes. At bottom of modmain.lua after AddReplicableComponent("leveler") function MyCharActions(act,inst) local old_Chop = GLOBAL.ACTIONS.CHOP.fn GLOBAL.ACTIONS.CHOP.fn = function(act) if act.doer.prefab == "mychar" then if act.doer.transformed == true then return else if act.doer.components.leveler ~= nil then act.doer.components.leveler:DoDelta("mycharlevelbadge", 0.1) --crashes here end end end return old_Chop(act) end end AddSimPostInit(MyCharActions) The AddLevelSystem() is in mychar.lua file, so I'm guessing it's not yet set up at this point. How would I do it then? Thanks. Link to comment Share on other sites More sharing options...
Kzisor Posted February 3, 2016 Share Posted February 3, 2016 (edited) 15 hours ago, SenL said: I found a bug on mine (not yours) because each kill is not set to x (static number). Each kill is variable from victim health * victim damage divided by some arbitrary number. Because of this when I kill a treeguard for example I will get 940% exp. I was hoping it would auto-level until it's < 100%. Now how do I do that... That isn't something that most level systems need to worry about because you don't see a level 1 killing a level 10 monster which would cause exponential experience growth. In order to combat this effect you would simply need to modify the onlevelup function to the following. local function onlevelup( inst, data ) if data.system == LEVEL_SYSTEM then if inst.components.leveler:GetLevel( LEVEL_SYSTEM ) < MAX_LEVEL then inst.components.leveler:SetLevel( LEVEL_SYSTEM, ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) + 1 ) ) local max_exp = ( ( inst.components.leveler:GetLevel( LEVEL_SYSTEM ) * START_EXPERIENCE ) * EXPERIENCE_MULT_MAX ) inst.components.leveler:SetMaxExperience( LEVEL_SYSTEM, max_exp ) -- Sets the max experience needed for the next level. if (data.experience >= max_exp) then -- This should be inclusive >= inst.components.leveler:DoDelta( LEVEL_SYSTEM, data.experience ) -- Will continue level growth. else inst.components.leveler:SetExperience( LEVEL_SYSTEM, data.experience ) -- Needs to be adjusted based on current experience over current level. determinestats( inst ) -- Will determine the stat increases based on leveling. -- Sets the new max values here, allows you to use determine stats in other places without setting max health every time. inst.components.health:DoDelta( inst.components.health.maxhealth ) inst.components.hunger:DoDelta( inst.components.hunger.max ) inst.components.sanity:DoDelta( inst.components.sanity.max ) end end end end 2 hours ago, SenL said: Hi Kzisor, I'm trying to give exp when chopping but I can't seem to figure it out. It crashes. At bottom of modmain.lua after AddReplicableComponent("leveler") The AddLevelSystem() is in mychar.lua file, so I'm guessing it's not yet set up at this point. How would I do it then? Thanks. Actions take place on the Client, components are not loaded on the client; this is why it's crashing. You need to set up a ModRPC function and inside the action function, you would SendModRPCToServer with the ModRPC which you created. local function ExpOnChop(inst) if inst.components.leveler then inst.components.leveler:DoDelta( LEVEL_SYSTEM, CHOP_EXPERIENCE ) end end AddModRPCHandler(modname, "ChopExp", ExpOnChop) local function GetModRPC( namespace, name ) return MOD_RPC[namespace][name] end local _Chop = GLOBAL.ACTIONS.CHOP.fn GLOBAL.ACTIONS.CHOP.fn = function(act) if act.doer:HasTag("[INSERT TAG HERE]") then if not act.doer.transformed then SendModRPCToServer( GetModRPC( modname, "ChopExp" ) ) end end return _Chop( act ) end You should not be using AddSimPostInit in Don't Starve Together; it's been depreciated in this version of the game. Edited February 3, 2016 by Kzisor Fixed code error. Link to comment Share on other sites More sharing options...
SenL Posted February 3, 2016 Author Share Posted February 3, 2016 Oh man, I would have never figured out how. Lol. I copied from "Starve Wars" btw, but they have TheForce component. Ok let me try those... Link to comment Share on other sites More sharing options...
Kzisor Posted February 3, 2016 Share Posted February 3, 2016 48 minutes ago, SenL said: Oh man, I would have never figured out how. Lol. I copied from "Starve Wars" btw, but they have TheForce component. Ok let me try those... Please let me know if you have anymore questions. Link to comment Share on other sites More sharing options...
SenL Posted February 3, 2016 Author Share Posted February 3, 2016 I'm doing something wrong. Crashes on SendModRPCToServer() modutil.lua 442: attempt to index local 'id_table' (a nil value) Code: local function ExpOnChopFn(inst) if inst.components.leveler ~= nil then inst.components.leveler:DoDelta("mycharlevelbadge", config_chopexp) end end AddModRPCHandler("gggroot", "ChopExp", ExpOnChop) local old_Chop = GLOBAL.ACTIONS.CHOP.fn GLOBAL.ACTIONS.CHOP.fn = function(act) if act.doer.prefab == "mychar" then if act.doer.transformed == true then return else SendModRPCToServer(GLOBAL.GetModRPCHandler("mychar", "ChopExp")) end end return old_Chop(act) end Notes/questions: 1) I had to add "GLOBAL." before GetModRPCHandler() otherwise it crashes there. Am I missing something? 2) How come the AddModRPCHandler uses ExpOnChop where the function name is ExpOnChopFn? (I tried both but still same crash) Thanks for your continuous help! Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now