This commit is contained in:
lifestorm
2024-08-05 18:40:29 +03:00
parent 9f505a0646
commit c6d9b6f580
8044 changed files with 1853472 additions and 21 deletions

View File

@@ -0,0 +1,180 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- registers the actions as ix.action and ix.action.list
-- registers the functions:
-- ix.action.Getactions() - returns actions on character
-- ix.action.RegisterSkillAction(uniqueID, data) - registers action specified
-- ix.action.Find(action) - finds the action specified
ix.action = ix.action or {}
ix.action.list = ix.action.list or {}
local isfunction = isfunction
function ix.action:RegisterSkillAction(skillID, uniqueID, data)
if (!uniqueID or type(uniqueID) != "string") then
ErrorNoHalt("Attempted to register action without valid uniqueID.")
return
end
data.uniqueID = uniqueID
data.name = data.name or "Unknown"
data.description = data.description or "No description given."
if (data.DoResult and type(data.DoResult) == "table" and data.DoResult[1]) then
table.SortByMember(data.DoResult, "level", true)
local copy = {}
for k, v in pairs(data.DoResult) do
copy[v.level] = v.exp
end
local currExp = 0
data.DoResult = {}
for i = 0, ix.skill.MAX_SKILL - 1 do
if (copy[i]) then currExp = copy[i] end
data.DoResult[i] = currExp
end
data.DoResult[ix.skill.MAX_SKILL] = 0
end
if (!skillID or !ix.skill:Find(skillID)) then
ErrorNoHalt("Attempted to register '"..uniqueID.."' action without valid skill.")
return
else
data.skill = ix.skill:Find(skillID).uniqueID
ix.skill:RegisterSkillAction(data.skill, data)
end
ix.action.list[uniqueID] = data
end
function ix.action:Find(actionID)
if (ix.action.list[actionID]) then
return ix.action.list[actionID]
else
for _, action in pairs(ix.action.list) do
if (string.find(action.name:utf8lower(), actionID:utf8lower())) then
return action
end
end
end
end
if (SERVER) then
ix.log.AddType("actionsDoAction", function(client, name, result, skill)
return string.format("%s has performed the '%s' action, gaining %d experience in %s.", client:GetName(), name, result, skill)
end)
ix.log.AddType("actionsDoActionNoExp", function(client, name)
return string.format("%s has performed the '%s' action.", client:GetName(), name)
end)
end
do
local CHAR = ix.meta.character
function CHAR:CanDoAction(actionID, ...)
local action = ix.action:Find(actionID)
if (!action) then
--ErrorNoHalt("Attempted to check if player can do invalid action '"..(actionID or "nil").."'.")
return
end
if (!action.CanDo) then
return true
end
if (!isfunction(action.CanDo)) then
return self:GetSkillLevel(action.skill) >= action.CanDo
else
return action:CanDo(self, self:GetSkillLevel(action.skill), ...)
end
end
if (SERVER) then
function CHAR:DoAction(actionID, ...)
local action = ix.action:Find(actionID)
if (!action) then
ErrorNoHalt(string.format("[ACTIONS] Attempted to do invalid action '%s'.", actionID))
end
local result = self:GetResult(actionID, ...)
if (result and result > 0 and self:GetSkillLevel(action.skill) >= 0) then
if (self:GetSkill(action.skill) >= ix.skill.MAX_SKILL) then
return
end
local storedExp = self:GetSkillStoredExp(action.skill)
self:SetSkillStoredExp(action.skill, math.min(result, 1999 - storedExp))
if (self:GetSkillAutoLevel(action.skill)) then
self:LevelUpSkill(action.skill)
elseif (self:CanLevelSkill(action.skill) and storedExp < 1000) then
local skill = ix.skill:Find(action.skill)
self:GetPlayer():NotifyLocalized("levelPossible", skill.name, self:GetSkill(skill.uniqueID) + 1)
end
if (!action.bNoLog) then
ix.log.Add(self:GetPlayer(), "actionsDoAction", action.name, result, action.skill)
end
elseif (action.alwaysLog) then
ix.log.Add(self:GetPlayer(), "actionsDoActionNoExp", action.name)
end
end
end
local function GetExp(DoResult, skillLevel)
for k, v in ipairs(DoResult) do
if (v.level > skillLevel) then
break
elseif (v.level <= skillLevel and skillLevel < DoResult[k + 1].level) then
return v.exp
end
end
return 0
end
function CHAR:GetResult(actionID, ...)
local action = ix.action:Find(actionID)
if (!action) then
--ErrorNoHalt("Attempted to get result from invalid action '"..(actionID or "nil").."'.")
return
end
if (!action.DoResult) then
return 0
end
local min = self:GetSkill(action.skill)
local max = math.max(min, self:GetSkillLevel(action.skill))
if (!isfunction(action.DoResult)) then
local maxExp = 0
for k = min, max do
if (action.DoResult[k] and action.DoResult[k] > maxExp) then
maxExp = action.DoResult[k]
end
end
return maxExp
else
local maxExp = 0
for i = min, max do
local result = action:DoResult(self, i, ...) or 0
if (result > maxExp) then
maxExp = result
end
end
return maxExp
end
end
end

View File

@@ -0,0 +1,245 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
-- registers the attributes as ix.special and ix.special.list
-- registers the functions:
-- ix.special.GetSpecial() - returns attributes on character
-- ix.special.RegisterAttributes(uniqueID, data) - registers attribute specified
-- ix.special.Find(attribute) - finds the attribute specified
ix.special = ix.special or {}
ix.special.list = ix.special.list or {}
if (SERVER) then
ix.log.AddType("specialBoostExtend", function(client, name, type, level)
return string.format("%s extended their level %d %s boost in %s.", client:GetName(), level, type, name)
end)
ix.log.AddType("specialBoostLevel", function(client, name, type, level)
return string.format("%s has gained a level %d %s boost in %s.", client:GetName(), level, type, name)
end)
ix.log.AddType("specialBoostTarget", function(client, name, type, level, current)
return string.format("%s has updated their %s %s boost target to %d (current : %d).", client:GetName(), name, type, level, current)
end)
ix.log.AddType("specialBoostWasted", function(client, name, type, level, current, target)
return string.format("%s has wasted a level %d %s boost in %s (current: %d; target: %d).", client:GetName(), level, type, name, current, target)
end)
end
ix.char.RegisterVar("special", {
default = {},
isLocal = true,
bNoDisplay = true,
field = "special",
fieldType = ix.type.text,
OnSet = function(character, attribute, value)
local special = ix.special:Find(attribute)
if (!special) then return end
local attributes = character:GetSpecial()
local client = character:GetPlayer()
attributes[special.uniqueID] = math.Clamp(value, 0, 10)
if (IsValid(client)) then
net.Start("ixAttributeTbl")
net.WriteUInt(character:GetID(), 32)
net.WriteString(special.uniqueID)
net.WriteInt(value, 8)
net.Send(client)
end
character.vars.special = attributes
end,
OnGet = function(character, key, default)
local data = character.vars.special or {}
if (key) then
if (!data) then return 0 end
return data[key] != nil and data[key] or 0
else
return data
end
end,
OnValidate = function(self, value, data, client)
if (value != nil) then
if (istable(value)) then
local count = 0
for _, v in pairs(value) do
count = count + v
end
if (count > (hook.Run("GetDefaultAttributePoints", client, data) or ix.config.Get("maxAttributes", 30))) then
return false, "unknownError"
end
else
return false, "unknownError"
end
end
end,
OnAdjust = function(self, client, data, value, newData)
newData.special = value
end
})
ix.char.RegisterVar("specialBoost", {
default = {},
isLocal = true,
bNoDisplay = true,
field = "special_boost",
fieldType = ix.type.text,
OnSet = function(character, attribute, value, bShort)
local client = character:GetPlayer()
if (!value and type(attribute) == "table") then
character.vars.specialBoost = attribute
if (IsValid(client)) then
net.Start("ixAttributeBoostTbl")
net.WriteUInt(character:GetID(), 32)
net.WriteTable(attribute)
net.Send(client)
end
return
end
local special = ix.special:Find(attribute)
if (!special) then return end
local boosts = character:GetSpecialBoost()
if (!boosts[special.uniqueID]) then
boosts[special.uniqueID] = {}
end
local index = bShort and "short" or "long"
if (!boosts[special.uniqueID][index]) then
boosts[special.uniqueID][index] = {
level = 0,
time = (bShort and 10) or 30,
target = value,
targetDuration = (bShort and 15 * 60) or 6 * 60
}
ix.log.Add(client, "specialBoostLevel", special.name, index, value)
else
local currentBoost = boosts[special.uniqueID][index]
if (currentBoost.level == value and currentBoost.target <= value) then
currentBoost.time = (bShort and 15 * 60) or 6 * 60
currentBoost.target = 0
currentBoost.targetDuration = 0
ix.log.Add(client, "specialBoostExtend", special.name, index, value)
elseif (currentBoost.level < value and currentBoost.target < value) then
if (currentBoost.target < currentBoost.level) then
currentBoost.time = (bShort and 10) or 30
end
currentBoost.target = value
currentBoost.targetDuration = (bShort and 15 * 60) or 6 * 60
ix.log.Add(client, "specialBoostLevel", special.name, index, value)
elseif (currentBoost.level > value and (currentBoost.target <= value or currentBoost.target == 0)) then
currentBoost.target = value
currentBoost.targetDuration = ((bShort and 15 * 60) or 6 * 60) - currentBoost.time
ix.log.Add(client, "specialBoostTarget", special.name, index, value, currentBoost.level)
else
ix.log.Add(client, "specialBoostWasted", special.name, index, value, currentBoost.level, currentBoost.target)
end
end
if (IsValid(client)) then
net.Start("ixAttributeBoostTbl")
net.WriteUInt(character:GetID(), 32)
net.WriteTable(boosts)
net.Send(client)
end
character.vars.specialBoost = boosts
end,
OnGet = function(character, attributeID)
local data = character.vars.specialBoost or {}
if (attributeID) then
if (!data or !data[attributeID]) then return 0 end
local short = (data[attributeID].short and data[attributeID].short.level) or 0
local long = (data[attributeID].long and data[attributeID].long.level) or 0
return short + long
else
return data
end
end
})
if (CLIENT) then
net.Receive("ixAttributeTbl", function()
local id = net.ReadUInt(32)
local character = ix.char.loaded[id]
if (character) then
character.vars.special = character.vars.special or {}
character.vars.special[net.ReadString()] = net.ReadInt(8)
end
end)
net.Receive("ixAttributeBoostTbl", function()
local id = net.ReadUInt(32)
local character = ix.char.loaded[id]
if (character) then
character.vars.specialBoost = net.ReadTable()
end
end)
else
util.AddNetworkString("ixAttributeTbl")
util.AddNetworkString("ixAttributeBoostTbl")
end
function ix.special:RegisterAttribute(uniqueID, data)
if (!uniqueID or type(uniqueID) != "string") then
ErrorNoHalt("Attempted to register attribute without valid uniqueID.")
return
end
data.uniqueID = uniqueID
data.name = data.name or "Unknown"
data.description = data.description or "No description given."
if (data.skills) then
for skillID, _ in pairs(data.skills) do
ix.skill:RegisterAttribute(skillID, data)
end
end
ix.special.list[uniqueID] = data
end
function ix.special:Find(attribute)
if (ix.special.list[attribute]) then
return ix.special.list[attribute]
else
attribute = string.utf8lower(attribute)
for _, data in pairs(ix.special.list) do
if (string.find(string.utf8lower(data.name), attribute)) then
return data
end
end
end
end
do
local CHAR = ix.meta.character
function CHAR:GetAttrBoostLevels(attribute, skill)
return self:GetBoostedAttribute(attribute.uniqueID) * (attribute.skills[skill.uniqueID] or 0)
end
function CHAR:GetBoostedAttribute(attributeID)
return math.Clamp(self:GetSpecial(attributeID) + self:GetSpecialBoost(attributeID), -10, 10)
end
end

View File

@@ -0,0 +1,376 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
local ix = ix
ix.recipe = ix.recipe or {}
ix.recipe.stored = ix.recipe.stored or {}
--[[
Begin defining the recipe class base for other recipe's to inherit from.
--]]
--[[ Set the __index meta function of the class. --]]
local CLASS_TABLE = {__index = CLASS_TABLE}
CLASS_TABLE.name = "Blueprint Base"
CLASS_TABLE.uniqueID = "blueprint_base"
CLASS_TABLE.skin = 0
CLASS_TABLE.model = "models/error.mdl"
CLASS_TABLE.category = "Other"
CLASS_TABLE.description = "A recipe with no description."
-- Called when the recipe is converted to a string.
function CLASS_TABLE:__tostring()
return "RECIPE["..self.name.."]"
end
--[[
A function to override an recipe's base data. This is
just a nicer way to set a value to go along with
the method of querying.
--]]
function CLASS_TABLE:Override(varName, value)
self[varName] = value
end
-- A function to register a new recipe.
function CLASS_TABLE:Register()
return ix.recipe:Register(self)
end
function CLASS_TABLE:PlayerCanCraftRecipe(client)
return ix.recipe:PlayerCanCraftRecipe(self, client)
end
--[[
End defining the base recipe class.
Begin defining the recipe utility functions.
--]]
-- A function to get all recipes.
function ix.recipe:GetAll()
return self.stored
end
-- A function to get a new recipe.
function ix.recipe:New()
local object = {}
setmetatable(object, CLASS_TABLE)
CLASS_TABLE.__index = CLASS_TABLE
return object
end
-- A function to register a new recipe.
function ix.recipe:Register(recipe)
recipe.uniqueID = string.utf8lower(string.gsub(recipe.uniqueID or string.gsub(recipe.name, "%s", "_"), "['%.]", ""))
self.stored[recipe.uniqueID] = recipe
if (recipe.model) then
util.PrecacheModel(recipe.model)
if (SERVER) then
resource.AddFile(recipe.model)
end
end
end
-- A function to get an recipe by its name.
function ix.recipe:FindByID(identifier)
if (identifier and identifier != 0 and type(identifier) != "boolean") then
if (self.stored[identifier]) then
return self.stored[identifier]
end
local lowerName = string.utf8lower(identifier)
local recipe = nil
for _, v in pairs(self.stored) do
local recipeName = v.name
if (string.find(string.utf8lower(recipeName), lowerName)
and (!recipe or string.utf8len(recipeName) < string.utf8len(recipe.name))) then
recipe = v
end
end
return recipe
end
end
function ix.recipe:Initialize()
local recipes = self:GetAll()
for _, v in pairs(recipes) do
if (v.OnSetup) then
v:OnSetup()
end
end
end
-- Called when a player attempts to craft an item.
function ix.recipe:PlayerCanCraftRecipe(recipe, client, inventory)
local character = client:GetCharacter()
inventory = inventory or character:GetInventory()
if (recipe.skill != "bartering" and !character:CanDoAction("recipe_"..recipe.uniqueID)) then
return false, "You do not have the required skill level to craft this recipe."
end
-- Check if the player has all the needed tools if there are any needed
if (recipe.tool and !istable(recipe.tool)) then
if !ix.item.list[recipe.tool] then
ErrorNoHalt("[Crafting] Recipe "..recipe.name.." has an unexisting tool "..recipe.tool.."!\n")
return false, "Recipe has unexisting tool "..recipe.tool.."!"
elseif (!inventory:HasItem(recipe.tool)) then
return false, "You do not have a "..ix.item.list[recipe.tool].name.."."
end
end
if recipe.tools and istable(recipe.tools) then
for _, v in pairs(recipe.tools) do
if ix.item.list[v] then
if (!inventory:HasItem(v)) then
return false, "You do not have a "..ix.item.list[v].name.."."
end
else
ErrorNoHalt("[Crafting] Recipe "..recipe.name.." has an unexisting tool "..v.."!\n")
return false, "Recipe has unexisting tool "..v.."!"
end
end
end
-- Check for the ingredients
if (recipe.ingredients) then
for ingredient, amount in pairs(recipe.ingredients) do
if (!ix.item.list[ingredient]) then
ErrorNoHalt("[Crafting] Recipe "..recipe.name.." has an unexisting ingredient "..ingredient.."!\n")
return false, "Recipe has unexisting ingredient "..ingredient.."!"
end
local count = inventory:GetItemCount(ingredient)
if (count == 0) then
return false, "You do not have any "..ix.item.list[ingredient].name.."."
elseif (count < amount) then
local name = ingredient
if (amount > 1) then
name = Schema:Pluralize(ix.item.list[ingredient].name)
end
return false, "You need at least "..tostring(amount).." "..name.."."
end
end
end
-- Check if player is near a crafting station if needed
if (recipe.station) then
if (!istable(recipe.station)) then
if (ix.item.list[recipe.station]) then
-- Find the entity the player is looking at
local entity = client:GetEyeTraceNoCursor().Entity
if (IsValid(entity)) then
if (entity:GetClass() != "ix_item") then
return false, "The attempted station is invalid!"
else
if !entity.GetItemTable or (entity.GetItemTable and !entity:GetItemTable()) then
return false, "This is an invalid type of station for this recipe!"
end
local itemTable = entity:GetItemTable()
if (itemTable.uniqueID != recipe.station) then
return false, "This is the wrong type of station for this recipe!"
end
if (client:GetShootPos():DistToSqr(entity:GetPos()) > 100 * 100) then
return false, "You need to be closer to the station!"
end
end
else
return false, "You need to be looking at a station!"
end
else
ErrorNoHalt("[Crafting] Recipe "..recipe.name.." has an unexisting station "..recipe.station.."!\n")
return false, "Recipe has unexisting station "..recipe.station.."!"
end
else
local validStations = {}
local bFound, bInvalidStationEntity = false, false
local entity = client:GetEyeTraceNoCursor().Entity
local bEntityValid = IsValid(entity)
if (!bEntityValid or client:GetShootPos():DistToSqr(entity:GetPos()) > 100 * 100) then
bInvalidStationEntity = true
end
local campFire = recipe.canUseCampfire
local campFireText = campFire and " or a Campfire" or ""
if (campFire and !bInvalidStationEntity and bEntityValid and entity:GetClass() == "ix_campfire") then
bFound = true
end
if (!bFound) then
if (entity:GetClass() != "ix_item" or !entity:GetItemTable()) then
bInvalidStationEntity = true
end
for _, v in pairs(recipe.station) do
if ix.item.list[v] then
validStations[#validStations + 1] = ix.item.list[v]:GetName()
if (bInvalidStationEntity) then continue end -- too far/invalid/wrong class, don't bother checking
local itemTable = entity:GetItemTable()
if (itemTable.uniqueID != v) then
continue
end
bFound = true
break -- valid station found, stop checking station loop
end
end
end
if (!bFound) then
return false, string.format("You need to be near and looking at a %s"..campFireText..".", table.concat(validStations, " or a "))
end
end
end
-- Check if player has the crafting license if needed
if (recipe.skill == "bartering" and !character:HasPermit(recipe.category)) then
return false, "You do not have a business permit for this category of items, contact the Combine Civil Administration."
end
if (recipe.cost and isnumber(recipe.cost)) then
local itemID = character:GetIdCard()
if (itemID) then
if (ix.item.instances[itemID]) then
local credits = ix.item.instances[itemID]:GetCredits()
if (credits < recipe.cost) then
return false, "You do not have enough credits to purchase this item!"
end
else
return false, "Could not find ID card!"
end
else
return false, "You do not have an active ID card!"
end
end
return true
end
if (CLIENT) then
function ix.recipe:GetIconInfo(recipe)
local model = recipe.model or "models/error.mdl"
local skin = recipe.skin
return model, skin
end
function ix.recipe:CanPlayerSeeRecipe(recipe)
if (recipe.hidden) then
return false
end
return true
end
else
function ix.recipe:PlayerCraftRecipe(recipe, client)
if (recipe.PlayerCraftRecipe) then
recipe:PlayerCraftRecipe(client)
return
end
local character = client:GetCharacter()
local inventory = character:GetInventory()
-- Take all the ingredients
for ingredient, amount in pairs(recipe.ingredients) do
for _ = 1, amount do
local item = inventory:HasItem(ingredient)
if (item) then
item:Remove()
else
break
end
end
end
-- Break tools
if (recipe.tools and !table.IsEmpty(recipe.tools)) then
for k, v in pairs(recipe.tools) do
local item = inventory:HasItem(v)
if (item and item.isTool) then
item:DamageDurability(1)
end
end
end
if (recipe.tool) then
local item = inventory:HasItem(recipe.tool)
if (item and item.isTool) then
item:DamageDurability(1)
end
end
-- Give the result
for result, amount in pairs(recipe.result) do
local item = ix.item.list[result]
if (!item) then continue end
local actualAmount
if (type(amount) == "table") then
actualAmount = amount[math.random(1, #amount)]
else
actualAmount = amount
end
if (item.maxStackSize) then
-- fill up existing stacks if possible
local items = inventory:GetItemsByUniqueID(result)
for _, v in ipairs(items) do
if (v:GetStackSize() < v.maxStackSize) then
local toAdd = math.min(v.maxStackSize - v:GetStackSize(), actualAmount)
actualAmount = actualAmount - toAdd
v:AddStack(toAdd)
if (actualAmount == 0) then
break
end
end
end
end
if (actualAmount > 0) then
for i = 1, actualAmount, (item.maxStackSize or 1) do
-- Give new items in stacks if possible
local data
if (item.maxStackSize) then
data = {stack = math.min(actualAmount + 1 - i, item.maxStackSize)}
end
if (!inventory:Add(result, 1, data)) then
ix.item.Spawn(result, client, nil, nil, data)
end
end
end
end
-- Set the player's next crafting time
client.ixNextCraftTime = CurTime() + 2
netstream.Start("CraftTime", client.ixNextCraftTime)
character:DoAction("recipe_"..recipe.uniqueID)
end
end

View File

@@ -0,0 +1,450 @@
--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]
ix.skill = ix.skill or {}
ix.skill.list = ix.skill.list or {}
ix.skill.scale = ix.skill.scale or {}
ix.skill.MAX_SKILL = 50
if (SERVER) then
ix.log.AddType("skillsGetSkill", function(client, name)
return string.format("%s has selected the %s skill.", client:GetName(), name)
end)
ix.log.AddType("skillsRemovedSkill", function(client, name, oldXP)
if (oldXP > 0) then
return string.format("%s has removed the %s skill (experience was: %d).", client:GetName(), name, oldXP)
else
return string.format("%s has removed the %s skill.", client:GetName(), name)
end
end)
ix.log.AddType("skillLevelUp", function(client, name, newExp)
return string.format("%s has gained level %d in the %s skill.", client:GetName(), newExp, name)
end)
end
ix.char.RegisterVar("skill", {
default = {},
isLocal = true,
bNoDisplay = true,
field = "skills",
fieldType = ix.type.text,
OnSet = function(character, skillID, level)
if (!skillID) then
ErrorNoHalt("Attempted to set level but no skillID was provided!")
return
end
local skill = ix.skill:Find(skillID)
if (!skill) then
ErrorNoHalt("Attempted to set level on invalid skill "..skillID.."!")
return
end
if (!level) then
level = character:GetSkill(skill.uniqueID) + 1
end
level = math.floor(level)
if (level < 0 or level > ix.skill.MAX_SKILL) then
ErrorNoHalt("Attempted to set invalid level '"..level.."' on "..skill.name.."'.")
return
end
local skills = character:GetSkill()
local totalLevel = character:GetTotalSkillLevel()
local currentLevel = skills[skill.uniqueID] or 0
if (currentLevel <= level and totalLevel - currentLevel + level > ix.config.Get("MaxTotalSkill")) then
ErrorNoHalt("Attempted to add level "..skill.name.." above total skill level!")
return
end
local client = character:GetPlayer()
if (IsValid(client) and (skills[skill.uniqueID] or 0) < level) then
ix.log.Add(client, "skillLevelUp", skill.name, level)
if (character:GetSkillAutoLevel(skill.uniqueID)) then
client:NotifyLocalized("levelGained", skill.name, character:GetSkill(skill.uniqueID) + 1)
end
net.Start("ixSkillLevelUpSound")
net.Send(client)
end
if (level > 0) then
skills[skill.uniqueID] = level
else
skills[skill.uniqueID] = nil
end
character.vars.skill = skills
if (IsValid(client)) then
net.Start("ixCharacterSkill")
net.WriteUInt(character:GetID(), 32)
net.WriteString(skill.uniqueID)
net.WriteUInt(skills[skill.uniqueID] or 0, 8)
net.Send(client)
end
if (level == ix.skill.MAX_SKILL) then
character:SetSkillStoredExp(skill.uniqueID)
end
end,
OnGet = function(character, skillID)
local skills = character.vars.skill or {}
if (skillID) then
if (!ix.skill:Find(skillID)) then
ErrorNoHalt("Attempted to get skill level in invalid skill "..skillID..".")
end
return skills[skillID] != nil and skills[skillID] or 0
else
return skills
end
end,
OnValidate = function(self, value, data, client)
if (value != nil) then
if (istable(value)) then
local count = 0
for _, v in pairs(value) do
count = count + v
end
if (count > 10) then
return false, "unknownError"
end
else
return false, "unknownError"
end
end
end,
OnAdjust = function(self, client, data, value, newData)
newData.skill = value
end
})
ix.char.RegisterVar("skillAutoLevel", {
default = {},
isLocal = true,
bNoDisplay = true,
field = "skills_autolevel",
fieldType = ix.type.text,
OnSet = function(character, skillID)
if (!skillID) then
ErrorNoHalt("Attempted to set experience but no skillID was provided!")
return
end
local skill = ix.skill:Find(skillID)
if (!skill) then
ErrorNoHalt("Attempted to set experience on invalid skill "..skillID.."!")
return
end
local skills = character:GetSkillAutoLevel()
skills[skill.uniqueID] = !skills[skill.uniqueID]
character.vars.skillAutoLevel = skills
-- Level skill if possible
if (skills[skill.uniqueID] == true) then
character:LevelUpSkill(skill.uniqueID)
end
local client = character:GetPlayer()
if (IsValid(client)) then
net.Start("ixCharacterSkillAutoLevel")
net.WriteUInt(character:GetID(), 32)
net.WriteString(skill.uniqueID)
net.WriteBool(skills[skill.uniqueID])
net.Send(client)
end
end,
OnGet = function(character, skillID)
local skills = character.vars.skillAutoLevel or {}
if (skillID) then
return skills[skillID] != nil and skills[skillID] or false
else
return skills
end
end
})
ix.char.RegisterVar("skillStoredExp", {
default = {},
isLocal = true,
bNoDisplay = true,
field = "skills_storedexp",
fieldType = ix.type.text,
OnSet = function(character, skillID, exp)
if (!skillID) then
ErrorNoHalt("Attempted to set stored experience but no skillID was provided!")
return
end
local skill = ix.skill:Find(skillID)
if (!skill) then
ErrorNoHalt("Attempted to set stored experience on invalid skill "..skillID.."!")
return
end
local skills = character:GetSkillStoredExp()
if (character:GetSkill(skill.uniqueID) >= ix.skill.MAX_SKILL) then
skills[skill.uniqueID] = nil
elseif (isbool(exp)) then
if (!skills[skill.uniqueID] or skills[skill.uniqueID] < 1000) then
ErrorNoHalt("Attempted to level "..skill.name.." without enough stored experience!")
return
end
skills[skill.uniqueID] = math.max(skills[skill.uniqueID] - 1000, 0)
if (skills[skill.uniqueID] == 0 or character:GetSkill(skill.uniqueID) >= ix.skill.MAX_SKILL) then
skills[skill.uniqueID] = nil
end
else
exp = exp and math.floor(exp)
if (!exp or exp < 0) then
ErrorNoHalt("Attempted to set "..skill.name.." to invalid stored experience ("..(exp or "nil")..")!")
return
end
if (character:GetSkill(skill.uniqueID) >= ix.skill.MAX_SKILL) then
ErrorNoHalt("Attempted to add stored experience to "..skill.name.." which is max level already!")
return
end
if (skills[skill.uniqueID] and skills[skill.uniqueID] + exp >= 2000) then
ErrorNoHalt("Attempted to add stored experience to "..skill.name.." above total stored experience ceiling!")
end
skills[skill.uniqueID] = math.min((skills[skill.uniqueID] or 0) + exp, 1999)
end
character.vars.skillStoredExp = skills
local client = character:GetPlayer()
if (IsValid(client)) then
net.Start("ixCharacterSkillStoredExp")
net.WriteUInt(character:GetID(), 32)
net.WriteString(skill.uniqueID)
net.WriteInt(skills[skill.uniqueID] or -1, 32)
net.Send(client)
end
end,
OnGet = function(character, skillID)
local skills = character.vars.skillStoredExp or {}
if (skillID) then
return skills[skillID] != nil and skills[skillID] or 0
else
return skills
end
end
})
if (CLIENT) then
net.Receive("ixCharacterSkill", function()
local id = net.ReadUInt(32)
local character = ix.char.loaded[id]
if (character) then
local skill = net.ReadString()
local level = net.ReadUInt(8)
local skills = character:GetSkill()
skills[skill] = (level != 0 and level) or nil
character.vars.skill = skills
if (IsValid(ix.gui.skills)) then
ix.gui.skills.skillPanels[skill].skillLevel:SetText("Skill Level: "..character:GetSkillLevel(skill).."/"..ix.skill.MAX_SKILL)
end
end
end)
net.Receive("ixCharacterSkillAutoLevel", function()
local id = net.ReadUInt(32)
local character = ix.char.loaded[id]
if (character) then
local skill = net.ReadString()
local skillAutoLevel = character:GetSkillAutoLevel()
skillAutoLevel[skill] = net.ReadBool()
character.vars.skillAutoLevel = skillAutoLevel
if (IsValid(ix.gui.skills)) then
ix.gui.skills.skillPanels[skill].autoLevel.autoLevel = skillAutoLevel[skill]
end
end
end)
net.Receive("ixCharacterSkillStoredExp", function()
local id = net.ReadUInt(32)
local character = ix.char.loaded[id]
if (character) then
local skill = net.ReadString()
local exp = net.ReadInt(32)
local skillStoredExp = character:GetSkillStoredExp()
skillStoredExp[skill] = (exp != -1 and exp) or nil
character.vars.skillStoredExp = skillStoredExp
if (IsValid(ix.gui.skills)) then
local progress = math.Clamp(character:GetSkillStoredExp(skill) / 1000, 0, 1)
ix.gui.skills.skillPanels[skill].progressBar.progress = progress
ix.gui.skills.skillPanels[skill].processLabel:SetupText(character)
end
end
end)
local levelCD = 0
net.Receive("ixSkillLevelUpSound", function()
if (levelCD < CurTime()) then
surface.PlaySound("willardnetworks/skills/levelup.mp3")
levelCD = CurTime() + 5
end
end)
else
util.AddNetworkString("ixCharacterSkill")
util.AddNetworkString("ixCharacterSkillAutoLevel")
util.AddNetworkString("ixCharacterSkillStoredExp")
util.AddNetworkString("ixSkillLevelUp")
util.AddNetworkString("ixSkillSetAutoLevel")
util.AddNetworkString("ixSkillLevelUpSound")
net.Receive("ixSkillLevelUp", function(len, ply)
local character = ply:GetCharacter()
local skill = net.ReadString()
if (character and character:CanLevelSkill(skill)) then
character:LevelUpSkill(skill)
end
end)
net.Receive("ixSkillSetAutoLevel", function(len, ply)
local character = ply:GetCharacter()
local skill = net.ReadString()
if (character) then
character:SetSkillAutoLevel(skill)
end
end)
end
function ix.skill:RegisterSkill(uniqueID, data)
if (!uniqueID or type(uniqueID) != "string") then
ErrorNoHalt("Attempted to register skill without valid uniqueID.")
return
end
data.uniqueID = uniqueID
data.name = data.name or "Unknown"
data.description = data.description or "No description given."
data.actions = data.actions or {}
data.attributes = data.attributes or {}
data.scale = data.scale or {}
ix.skill.list[uniqueID] = data
end
function ix.skill:RegisterSkillAction(skillID, action)
local skill = self:Find(skillID)
if (!skill) then return end
for k, v in ipairs(skill.actions) do
if (v.uniqueID == action.uniqueID) then
skill.actions[k] = action
return
end
end
skill.actions[#skill.actions + 1] = action
end
function ix.skill:RegisterAttribute(skillID, attribute)
local skill = self:Find(skillID)
if (!skill) then return end
for k, v in ipairs(skill.attributes) do
if (v.uniqueID == attribute.uniqueID) then
skill.attributes[k] = attribute
return
end
end
skill.attributes[#skill.attributes + 1] = attribute
end
function ix.skill:RegisterSkillScale(skillID, uniqueID, data)
local skill = self:Find(skillID)
if (!skill) then return end
data.uniqueID = uniqueID
data.name = data.name or "Unknown"
data.description = data.description or "No description given."
data.minLevel = data.minLevel or 0
data.maxLevel = data.maxLevel or self.MAX_SKILL
data.minValue = data.minValue or 0
data.increase = data.increase or 1
data.digits = data.digits or 0
data.skill = skill.uniqueID
if (ix.skill.scale[uniqueID]) then
for k, v in ipairs(skill.scale) do
if v.uniqueID == uniqueID then
skill.scale[k] = data
end
end
else
skill.scale[#skill.scale + 1] = data
end
ix.skill.scale[uniqueID] = data
-- Register "non-recipe" for current skill scale
local RECIPE = ix.recipe:New()
RECIPE.uniqueID = "skillscale_"..uniqueID
RECIPE.name = "Current "..data.name
RECIPE.category = "Level Unlocks"
RECIPE.noIngredients = true
RECIPE.skillScale = true
RECIPE.skillScaleID = uniqueID
RECIPE.level = 0
RECIPE.skill = skillID
RECIPE:Register()
end
function ix.skill:Find(skill)
-- finds a skill based on input by stripping caps on input and looking through DB recursive
if (ix.skill.list[skill]) then
return ix.skill.list[skill]
else
skill = string.utf8lower(skill)
for _, data in pairs(ix.skill.list) do
if (string.find(string.utf8lower(data.name), skill)) then
return data
end
end
end
end