Initial commit

This commit is contained in:
pavetr
2026-03-15 14:54:49 +03:00
commit 64f8029c06
4027 changed files with 254888 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
local PANEL = {}
local function canBuyFood(food)
local ply = LocalPlayer()
if (food.requiresCook == nil or food.requiresCook == true) and not ply:isCook() then return false, true end
if food.customCheck and not food.customCheck(LocalPlayer()) then return false, false end
if not ply:canAfford(food.price) then return false, false end
return true
end
function PANEL:generateButtons()
for _, v in pairs(FoodItems) do
local pnl = vgui.Create("F4MenuEntityButton", self)
pnl:setDarkRPItem(v)
pnl.DoClick = fn.Partial(RunConsoleCommand, "DarkRP", "buyfood", v.name)
self:AddItem(pnl)
end
end
function PANEL:shouldHide()
for _, v in pairs(FoodItems) do
local canBuy, important = canBuyFood(v)
if not self:isItemHidden(not canBuy, important) then return false end
end
return true
end
function PANEL:PerformLayout()
for _, v in pairs(self.Items) do
local canBuy, important = canBuyFood(v.DarkRPItem)
v:SetDisabled(not canBuy, important)
end
self.BaseClass.PerformLayout(self)
end
derma.DefineControl("F4MenuFood", "DarkRP F4 Food Tab", PANEL, "F4MenuEntitiesBase")
hook.Add("F4MenuTabs", "HungerMod_F4Tabs", function()
if not table.IsEmpty(FoodItems) then
DarkRP.addF4MenuTab(DarkRP.getPhrase("Food"), vgui.Create("F4MenuFood"))
end
end)

View File

@@ -0,0 +1,103 @@
local cvars = cvars
local draw = draw
local hook = hook
local math = math
local table = table
local timer = timer
local Color = Color
local ColorAlpha = ColorAlpha
local CreateClientConVar = CreateClientConVar
local GetConVar = GetConVar
local ipairs = ipairs
local pairs = pairs
local unpack = unpack
local ConVars = {
HungerBackground = {0, 0, 0, 255},
HungerForeground = {30, 30, 120, 255},
HungerPercentageText = {255, 255, 255, 255},
StarvingText = {200, 0, 0, 255},
FoodEatenBackground = {0, 0, 0}, -- No alpha
FoodEatenForeground = {20, 100, 20} -- No alpha
}
local HUDWidth = 0
local FoodAteAlpha = -1
local FoodAteY = 0
surface.CreateFont("HungerPlus", {
size = 70,
weight = 500,
antialias = true,
shadow = false,
font = "ChatFont",
extended = true,
})
local function ReloadConVars()
for name, Colour in pairs(ConVars) do
ConVars[name] = {}
for num, rgb in ipairs(Colour) do
local ConVarName = name .. num
local CVar = GetConVar(ConVarName) or CreateClientConVar(ConVarName, rgb, true, false)
table.insert(ConVars[name], CVar:GetInt())
if not cvars.GetConVarCallbacks(ConVarName, false) then
cvars.AddChangeCallback(ConVarName, function() timer.Simple(0, ReloadConVars) end)
end
end
ConVars[name] = Color(unpack(ConVars[name]))
end
if HUDWidth == 0 then
HUDWidth = 240
cvars.AddChangeCallback("HudW", function() timer.Simple(0, ReloadConVars) end)
end
HUDWidth = GetConVar("HudW") and GetConVar("HudW"):GetInt() or 240
end
timer.Simple(0, ReloadConVars)
local function HMHUD()
local shouldDraw = hook.Call("HUDShouldDraw", GAMEMODE, "DarkRP_Hungermod")
if shouldDraw == false then return end
local energy = math.ceil(LocalPlayer():getDarkRPVar("Energy") or 0)
local x = 5
local y = ScrH() - 9
local cornerRadius = 4
if energy > 0 then
cornerRadius = math.Min(4, (HUDWidth - 9) * (energy / 100) / 3 * 2 - (HUDWidth - 9) * (energy / 100) / 3 * 2 % 2)
end
draw.RoundedBox(cornerRadius, x - 1, y - 1, HUDWidth - 8, 9, ConVars.HungerBackground)
if energy > 0 then
draw.RoundedBox(cornerRadius, x, y, (HUDWidth - 9) * (energy / 100), 7, ConVars.HungerForeground)
draw.DrawNonParsedSimpleText(energy .. "%", "DefaultSmall", HUDWidth / 2, y - 3, ConVars.HungerPercentageText, 1)
else
draw.DrawNonParsedSimpleText(DarkRP.getPhrase("starving"), "ChatFont", HUDWidth / 2, y - 5, ConVars.StarvingText, 1)
end
if FoodAteAlpha > -1 then
local mul = 1
if FoodAteY <= ScrH() - 100 then
mul = -.5
end
draw.DrawNonParsedSimpleText("++", "HungerPlus", 208, FoodAteY + 1, ColorAlpha(ConVars.FoodEatenBackground, FoodAteAlpha), 0)
draw.DrawNonParsedSimpleText("++", "HungerPlus", 207, FoodAteY, ColorAlpha(ConVars.FoodEatenForeground, FoodAteAlpha), 0)
FoodAteAlpha = math.Clamp(FoodAteAlpha + 4 * FrameTime() * mul, -1, 1) --ColorAlpha works with 0-1 alpha
FoodAteY = FoodAteY - 150 * FrameTime()
end
end
hook.Add("HUDDrawTargetID", "HMHUD", HMHUD) --HUDDrawTargetID is called after DarkRP HUD is drawn in HUDPaint
local function AteFoodIcon(msg)
FoodAteAlpha = 1
FoodAteY = ScrH() - 8
end
usermessage.Hook("AteFoodIcon", AteFoodIcon)

View File

@@ -0,0 +1,5 @@
DarkRP.declareChatCommand{
command = "buyfood",
description = "Buy food.",
delay = 1.5
}

View File

@@ -0,0 +1,103 @@
local isnil = fn.Curry(fn.Eq, 2)(nil)
local validFood = {"name", model = isstring, "energy", "price", onEaten = fn.FOr{isnil, isfunction}}
FoodItems = {}
function DarkRP.createFood(name, mdl, energy, price)
local foodItem = istable(mdl) and mdl or {model = mdl, energy = energy, price = price}
foodItem.name = name
if DarkRP.DARKRP_LOADING and DarkRP.disabledDefaults["food"][name] then return end
for k, v in pairs(validFood) do
local isFunction = isfunction(v)
if (isFunction and not v(foodItem[k])) or (not isFunction and foodItem[v] == nil) then
ErrorNoHalt("Corrupt food \"" .. (name or "") .. "\": element " .. (isFunction and k or v) .. " is corrupt.\n")
end
end
table.insert(FoodItems, foodItem)
end
AddFoodItem = DarkRP.createFood
DarkRP.getFoodItems = fp{fn.Id, FoodItems}
function DarkRP.removeFoodItem(i)
local food = FoodItems[i]
FoodItems[i] = nil
hook.Run("onFoodItemRemoved", i, food)
end
local plyMeta = FindMetaTable("Player")
plyMeta.isCook = fn.Compose{fn.Curry(fn.GetValue, 2)("cook"), plyMeta.getJobTable}
--[[
Valid members:
model = string, -- the model of the food item
energy = int, -- how much energy it restores
price = int, -- the price of the food
requiresCook = boolean, -- whether only cooks can buy this food
customCheck = function(ply) return boolean end, -- customCheck on purchase function
customCheckMessage = string -- message to people who cannot buy it because of the customCheck
]]
DarkRP.DARKRP_LOADING = true
DarkRP.registerDarkRPVar("Energy", net.WriteFloat, net.ReadFloat)
DarkRP.createFood("Banana", {
model = "models/props/cs_italy/bananna.mdl",
energy = 10,
price = 10
})
DarkRP.createFood("Bunch of bananas", {
model = "models/props/cs_italy/bananna_bunch.mdl",
energy = 20,
price = 20
})
DarkRP.createFood("Melon", {
model = "models/props_junk/watermelon01.mdl",
energy = 20,
price = 20
})
DarkRP.createFood("Glass bottle", {
model = "models/props_junk/GlassBottle01a.mdl",
energy = 20,
price = 20
})
DarkRP.createFood("Pop can", {
model = "models/props_junk/PopCan01a.mdl",
energy = 5,
price = 5
})
DarkRP.createFood("Plastic bottle", {
model = "models/props_junk/garbage_plasticbottle003a.mdl",
energy = 15,
price = 15
})
DarkRP.createFood("Milk", {
model = "models/props_junk/garbage_milkcarton002a.mdl",
energy = 20,
price = 20
})
DarkRP.createFood("Bottle 1", {
model = "models/props_junk/garbage_glassbottle001a.mdl",
energy = 10,
price = 10
})
DarkRP.createFood("Bottle 2", {
model = "models/props_junk/garbage_glassbottle002a.mdl",
energy = 10,
price = 10
})
DarkRP.createFood("Bottle 3", {
model = "models/props_junk/garbage_glassbottle003a.mdl",
energy = 10,
price = 10
})
DarkRP.createFood("Orange", {
model = "models/props/cs_italy/orange.mdl",
energy = 20,
price = 20
})
DarkRP.DARKRP_LOADING = nil

View File

@@ -0,0 +1,88 @@
DarkRP.createFood = DarkRP.stub{
name = "createFood",
description = "Create food for DarkRP.",
parameters = {
{
name = "name",
description = "The name of the food.",
type = "string",
optional = false
},
{
name = "tbl",
description = "Table containing the information for the food.",
type = "table",
optional = false
}
},
returns = {
},
metatable = DarkRP
}
AddFoodItem = DarkRP.createFood
DarkRP.removeFoodItem = DarkRP.stub{
name = "removeFoodItem",
description = "Remove a food item from DarkRP. NOTE: Must be called from BOTH server AND client to properly get it removed!",
parameters = {
{
name = "i",
description = "The index of the item.",
type = "number",
optional = false
}
},
returns = {
},
metatable = DarkRP
}
DarkRP.hookStub{
name = "onFoodItemRemoved",
description = "Called when a food item is removed.",
parameters = {
{
name = "num",
description = "The index of this item.",
type = "number"
},
{
name = "itemTable",
description = "The table containing all the info about this item.",
type = "table"
}
},
returns = {
}
}
DarkRP.PLAYER.isCook = DarkRP.stub{
name = "isCook",
description = "Whether this player is a cook. This function is only available if hungermod is enabled.",
parameters = {
},
returns = {
{
name = "answer",
description = "Whether this player is a cook.",
type = "boolean"
}
},
metatable = DarkRP.PLAYER
}
DarkRP.getFoodItems = DarkRP.stub{
name = "getFoodItems",
description = "Get all food items.",
parameters = {
},
returns = {
{
name = "set",
description = "Table with food items.",
type = "table"
}
},
metatable = DarkRP
}

View File

@@ -0,0 +1,103 @@
local function HMPlayerSpawn(ply)
ply:setSelfDarkRPVar("Energy", 100)
end
hook.Add("PlayerSpawn", "HMPlayerSpawn", HMPlayerSpawn)
local function HMThink()
for _, v in ipairs(player.GetAll()) do
if not v:Alive() then continue end
v:hungerUpdate()
end
end
timer.Create("HMThink", 10, 0, HMThink)
local function HMPlayerInitialSpawn(ply)
ply:newHungerData()
end
hook.Add("PlayerInitialSpawn", "HMPlayerInitialSpawn", HMPlayerInitialSpawn)
local function HMAFKHook(ply, afk)
if afk then
ply.preAFKHunger = ply:getDarkRPVar("Energy")
else
ply:setSelfDarkRPVar("Energy", ply.preAFKHunger or 100)
ply.preAFKHunger = nil
end
end
hook.Add("playerSetAFK", "Hungermod", HMAFKHook)
local function BuyFood(ply, args)
if args == "" then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
return ""
end
for _, v in pairs(FoodItems) do
if string.lower(args) ~= string.lower(v.name) then continue end
if (v.requiresCook == nil or v.requiresCook == true) and not ply:isCook() then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("unable", "/buyfood", DarkRP.getPhrase("cooks_only")))
return ""
end
if v.customCheck and not v.customCheck(ply) then
if v.customCheckMessage then
DarkRP.notify(ply, 1, 4, v.customCheckMessage)
end
return ""
end
local foodTable = {
cmd = "buyfood",
max = GAMEMODE.Config.maxfooditems
}
if ply:customEntityLimitReached(foodTable) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("limit", GAMEMODE.Config.chatCommandPrefix .. "buyfood"))
return ""
end
ply:addCustomEntity(foodTable)
local cost = v.price
if not ply:canAfford(cost) then
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("cant_afford", DarkRP.getPhrase("food")))
return ""
end
ply:addMoney(-cost)
DarkRP.notify(ply, 0, 4, DarkRP.getPhrase("you_bought", v.name, DarkRP.formatMoney(cost), ""))
local trace = {}
trace.start = ply:EyePos()
trace.endpos = trace.start + ply:GetAimVector() * 85
trace.filter = ply
local tr = util.TraceLine(trace)
local SpawnedFood = ents.Create("spawned_food")
SpawnedFood.DarkRPItem = foodTable
SpawnedFood:Setowning_ent(ply)
SpawnedFood:SetPos(tr.HitPos)
SpawnedFood.onlyremover = true
SpawnedFood.SID = ply.SID
SpawnedFood:SetModel(v.model)
-- for backwards compatibility
SpawnedFood.FoodName = v.name
SpawnedFood.FoodEnergy = v.energy
SpawnedFood.FoodPrice = v.price
SpawnedFood.foodItem = v
SpawnedFood:Spawn()
DarkRP.placeEntity(SpawnedFood, tr, ply)
hook.Call("playerBoughtFood", nil, ply, v, SpawnedFood, cost)
return ""
end
DarkRP.notify(ply, 1, 4, DarkRP.getPhrase("invalid_x", DarkRP.getPhrase("arguments"), ""))
return ""
end
DarkRP.defineChatCommand("buyfood", BuyFood)

View File

@@ -0,0 +1,86 @@
DarkRP.PLAYER.newHungerData = DarkRP.stub{
name = "newHungerData",
description = "Create the initial hunger data (called on PlayerInitialSpawn).",
parameters = {
},
returns = {
},
metatable = DarkRP.PLAYER
}
DarkRP.PLAYER.hungerUpdate = DarkRP.stub{
name = "hungerUpdate",
description = "Makes the player slightly more hungry. Called in a timer by default.",
parameters = {
},
returns = {
},
metatable = DarkRP.PLAYER
}
DarkRP.hookStub{
name = "playerBoughtFood",
description = "Called when a player bought food.",
parameters = {
{
name = "ply",
description = "The player who bought food.",
type = "Player"
},
{
name = "food",
description = "Food table.",
type = "table"
},
{
name = "spawnedfood",
description = "Entity of spawned food.",
type = "Entity"
},
{
name = "cost",
description = "How much player paid.",
type = "number"
}
},
returns = {
}
}
DarkRP.hookStub{
name = "hungerUpdate",
description = "Called every 10 seconds for every player when hungermod is on. This updates the player's hunger level.",
parameters = {
{
name = "ply",
description = "The player who might be slightly more hungery.",
type = "Player"
},
{
name = "energy",
description = "The energy the player has left.",
type = "number"
}
},
returns = {
{
name = "override",
description = "Override the default behaviour of substracting some and killing the player when starving.",
type = "boolean"
}
}
}
DarkRP.hookStub{
name = "playerStarved",
description = "Called when a player dies of starvation.",
parameters = {
{
name = "ply",
description = "The player who died of starvation.",
type = "Player"
}
},
returns = {
}
}

View File

@@ -0,0 +1,33 @@
local meta = FindMetaTable("Player")
function meta:newHungerData()
self:setSelfDarkRPVar("Energy", 100)
end
function meta:hungerUpdate()
if not GAMEMODE.Config.hungerspeed then return end
local energy = self:getDarkRPVar("Energy")
local override = hook.Call("hungerUpdate", nil, self, energy)
if override then return end
self:setSelfDarkRPVar("Energy", energy and math.Clamp(energy - GAMEMODE.Config.hungerspeed, 0, 100) or 100)
if self:getDarkRPVar("Energy") == 0 then
local health = self:Health()
local dmg = DamageInfo()
dmg:SetDamage(GAMEMODE.Config.starverate)
dmg:SetInflictor(self)
dmg:SetAttacker(self)
dmg:SetDamageType(bit.bor(DMG_DISSOLVE, DMG_NERVEGAS))
self:TakeDamageInfo(dmg)
if health - GAMEMODE.Config.starverate <= 0 then
self.Slayed = true
hook.Call("playerStarved", nil, self)
end
end
end