add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
local animationTime = 2
local PLUGIN = PLUGIN
PLUGIN.cameraFraction = 0
local function GetHeadBone(client)
local head
for i = 1, client:GetBoneCount() do
local name = client:GetBoneName(i)
if (string.find(name:lower(), "head")) then
head = i
break
end
end
return head
end
function PLUGIN:PlayerBindPress(client, bind, bPressed)
if (!client:GetNetVar("actEnterAngle")) then
return
end
if (bind:find("+jump") and bPressed) then
ix.command.Send("ExitAct")
return true
end
end
function PLUGIN:ShouldDrawLocalPlayer(client)
if (client:GetNetVar("actEnterAngle") and self.cameraFraction > 0.25) then
return true
elseif (self.cameraFraction > 0.25) then
return true
end
end
local forwardOffset = 16
local backwardOffset = -32
local heightOffset = Vector(0, 0, 20)
local idleHeightOffset = Vector(0, 0, 6)
local traceMin = Vector(-4, -4, -4)
local traceMax = Vector(4, 4, 4)
function PLUGIN:CalcView(client, origin)
local enterAngle = client:GetNetVar("actEnterAngle")
local fraction = self.cameraFraction
local offset = self.bIdle and forwardOffset or backwardOffset
local height = self.bIdle and idleHeightOffset or heightOffset
if (!enterAngle) then
if (fraction > 0) then
local view = {
origin = LerpVector(fraction, origin, origin + self.forward * offset + height)
}
if (self.cameraTween) then
self.cameraTween:update(FrameTime())
end
return view
end
return
end
local view = {}
local forward = enterAngle:Forward()
local head = GetHeadBone(client)
local bFirstPerson = true
if (ix.option.Get("thirdpersonEnabled", false)) then
local originPosition = head and client:GetBonePosition(head) or client:GetPos()
-- check if the camera will hit something
local data = util.TraceHull({
start = originPosition,
endpos = originPosition - client:EyeAngles():Forward() * 48,
mins = traceMin * 0.75,
maxs = traceMax * 0.75,
filter = client
})
bFirstPerson = data.Hit
if (!bFirstPerson) then
view.origin = data.HitPos
end
end
if (bFirstPerson) then
if (head) then
local position = client:GetBonePosition(head) + forward * offset + height
local data = {
start = (client:GetBonePosition(head) or Vector(0, 0, 64)) + forward * 8,
endpos = position + forward * offset,
mins = traceMin,
maxs = traceMax,
filter = client
}
data = util.TraceHull(data)
if (data.Hit) then
view.origin = data.HitPos
else
view.origin = position
end
else
view.origin = origin + forward * forwardOffset + height
end
end
view.origin = LerpVector(fraction, origin, view.origin)
if (self.cameraTween) then
self.cameraTween:update(FrameTime())
end
return view
end
net.Receive("ixActEnter", function()
PLUGIN.bIdle = net.ReadBool()
PLUGIN.forward = LocalPlayer():GetNetVar("actEnterAngle"):Forward()
PLUGIN.cameraTween = ix.tween.new(animationTime, PLUGIN, {
cameraFraction = 1
}, "outQuint")
end)
net.Receive("ixActLeave", function()
PLUGIN.cameraTween = ix.tween.new(animationTime * 0.5, PLUGIN, {
cameraFraction = 0
}, "outQuint")
end)

View File

@@ -0,0 +1,166 @@
local function FacingWall(client)
local data = {}
data.start = client:EyePos()
data.endpos = data.start + client:GetForward() * 20
data.filter = client
if (!util.TraceLine(data).Hit) then
return "@faceWall"
end
end
local function FacingWallBack(client)
local data = {}
data.start = client:LocalToWorld(client:OBBCenter())
data.endpos = data.start - client:GetForward() * 20
data.filter = client
if (!util.TraceLine(data).Hit) then
return "@faceWallBack"
end
end
function PLUGIN:SetupActs()
-- sit
ix.act.Register("Sit", {"citizen_male", "citizen_female"}, {
start = {"idle_to_sit_ground", "idle_to_sit_chair"},
sequence = {"sit_ground", "sit_chair"},
finish = {
{"sit_ground_to_idle", duration = 2.1},
""
},
untimed = true,
idle = true
})
ix.act.Register("SitWall", {"citizen_male", "citizen_female"}, {
sequence = {
{"plazaidle4", check = FacingWallBack},
{"injured1", check = FacingWallBack, offset = function(client)
return client:GetForward() * 14
end}
},
untimed = true,
idle = true
})
ix.act.Register("Sit", "vortigaunt", {
sequence = "chess_wait",
untimed = true,
idle = true
})
-- stand
ix.act.Register("Stand", "citizen_male", {
sequence = {"lineidle01", "lineidle02", "lineidle03", "lineidle04"},
untimed = true,
idle = true
})
ix.act.Register("Stand", "citizen_female", {
sequence = {"lineidle01", "lineidle02", "lineidle03"},
untimed = true,
idle = true
})
ix.act.Register("Stand", "metrocop", {
sequence = "plazathreat2"
})
-- cheer
ix.act.Register("Cheer", "citizen_male", {
sequence = {{"cheer1", duration = 1.6}, "cheer2", "wave_smg1"}
})
ix.act.Register("Cheer", "citizen_female", {
sequence = {"cheer1", "wave_smg1"}
})
-- lean
ix.act.Register("Lean", {"citizen_male", "citizen_female"}, {
start = {"idle_to_lean_back", "", ""},
sequence = {
{"lean_back", check = FacingWallBack},
{"plazaidle1", check = FacingWallBack},
{"plazaidle2", check = FacingWallBack}
},
untimed = true,
idle = true
})
ix.act.Register("Lean", {"metrocop"}, {
sequence = {{"idle_baton", check = FacingWallBack}, "busyidle2"},
untimed = true,
idle = true
})
-- injured
ix.act.Register("Injured", "citizen_male", {
sequence = {"d1_town05_wounded_idle_1", "d1_town05_wounded_idle_2", "d1_town05_winston_down"},
untimed = true,
idle = true
})
ix.act.Register("Injured", "citizen_female", {
sequence = "d1_town05_wounded_idle_1",
untimed = true,
idle = true
})
-- arrest
ix.act.Register("ArrestWall", "citizen_male", {
sequence = {
{"apcarrestidle",
check = FacingWall,
offset = function(client)
return -client:GetForward() * 23
end},
"spreadwallidle"
},
untimed = true
})
ix.act.Register("Arrest", "citizen_male", {
sequence = "arrestidle",
untimed = true
})
-- threat
ix.act.Register("Threat", "metrocop", {
sequence = "plazathreat1",
})
-- deny
ix.act.Register("Deny", "metrocop", {
sequence = "harassfront2",
})
-- motion
ix.act.Register("Motion", "metrocop", {
sequence = {"motionleft", "motionright", "luggagewarn"}
})
-- wave
ix.act.Register("Wave", {"citizen_male", "citizen_female"}, {
sequence = {{"wave", duration = 2.75}, {"wave_close", duration = 1.75}}
})
-- pant
ix.act.Register("Pant", {"citizen_male", "citizen_female"}, {
start = {"d2_coast03_postbattle_idle02_entry", "d2_coast03_postbattle_idle01_entry"},
sequence = {"d2_coast03_postbattle_idle02", {"d2_coast03_postbattle_idle01", check = FacingWall}},
untimed = true
})
-- window
ix.act.Register("Window", "citizen_male", {
sequence = "d1_t03_tenements_look_out_window_idle",
untimed = true
})
ix.act.Register("Window", "citizen_female", {
sequence = "d1_t03_lookoutwindow",
untimed = true
})
end

View File

@@ -0,0 +1,259 @@
--[[--
Provides players the ability to perform animations.
]]
-- @module ix.act
local PLUGIN = PLUGIN
PLUGIN.name = "Player Acts"
PLUGIN.description = "Adds animations that can be performed by certain models."
PLUGIN.author = "`impulse"
ix.act = ix.act or {}
ix.act.stored = ix.act.stored or {}
CAMI.RegisterPrivilege({
Name = "Helix - Player Acts",
MinAccess = "user"
})
--- Registers a sequence as a performable animation.
-- @realm shared
-- @string name Name of the animation (in CamelCase)
-- @string modelClass Model class to add this animation to
-- @tab data An `ActInfoStructure` table describing the animation
function ix.act.Register(name, modelClass, data)
ix.act.stored[name] = ix.act.stored[name] or {} -- might be adding onto an existing act
if (!data.sequence) then
return ErrorNoHalt(string.format(
"Act '%s' for '%s' tried to register without a provided sequence\n", name, modelClass
))
end
if (!istable(data.sequence)) then
data.sequence = {data.sequence}
end
if (data.start and istable(data.start) and #data.start != #data.sequence) then
return ErrorNoHalt(string.format(
"Act '%s' tried to register without matching number of enter sequences\n", name
))
end
if (data.finish and istable(data.finish) and #data.finish != #data.sequence) then
return ErrorNoHalt(string.format(
"Act '%s' tried to register without matching number of exit sequences\n", name
))
end
if (istable(modelClass)) then
for _, v in ipairs(modelClass) do
ix.act.stored[name][v] = data
end
else
ix.act.stored[name][modelClass] = data
end
end
--- Removes a sequence from being performable if it has been previously registered.
-- @realm shared
-- @string name Name of the animation
function ix.act.Remove(name)
ix.act.stored[name] = nil
ix.command.list["Act" .. name] = nil
end
ix.util.Include("sh_definitions.lua")
ix.util.Include("sv_hooks.lua")
ix.util.Include("cl_hooks.lua")
function PLUGIN:InitializedPlugins()
hook.Run("SetupActs")
hook.Run("PostSetupActs")
end
function PLUGIN:ExitAct(client)
client.ixUntimedSequence = nil
client:SetNetVar("actEnterAngle")
net.Start("ixActLeave")
net.Send(client)
end
function PLUGIN:PostSetupActs()
-- create chat commands for all stored acts
for act, classes in pairs(ix.act.stored) do
local variants = 1
local COMMAND = {
privilege = "Player Acts"
}
-- check if this act has any variants (i.e /ActSit 2)
for _, v in pairs(classes) do
if (#v.sequence > 1) then
variants = math.max(variants, #v.sequence)
end
end
-- setup command arguments if there are variants for this act
if (variants > 1) then
COMMAND.arguments = bit.bor(ix.type.number, ix.type.optional)
COMMAND.argumentNames = {"variant (1-" .. variants .. ")"}
end
COMMAND.GetDescription = function(command)
return L("cmdAct", act)
end
local privilege = "Helix - " .. COMMAND.privilege
-- we'll perform a model class check in OnCheckAccess to prevent the command from showing up on the client at all
COMMAND.OnCheckAccess = function(command, client)
local bHasAccess, _ = CAMI.PlayerHasAccess(client, privilege, nil)
if (!bHasAccess) then
return false
end
local modelClass = ix.anim.GetModelClass(client:GetModel())
if (!classes[modelClass]) then
return false, "modelNoSeq"
end
return true
end
COMMAND.OnRun = function(command, client, variant)
variant = math.Clamp(tonumber(variant) or 1, 1, variants)
if (client:GetNetVar("actEnterAngle")) then
return "@notNow"
end
local modelClass = ix.anim.GetModelClass(client:GetModel())
local bCanEnter, error = PLUGIN:CanPlayerEnterAct(client, modelClass, variant, classes)
if (!bCanEnter) then
return error
end
local data = classes[modelClass]
local mainSequence = data.sequence[variant]
local mainDuration
-- check if the main sequence has any extra info
if (istable(mainSequence)) then
-- any validity checks to perform (i.e facing a wall)
if (mainSequence.check) then
local result = mainSequence.check(client)
if (result) then
return result
end
end
-- position offset
if (mainSequence.offset) then
client.ixOldPosition = client:GetPos()
client:SetPos(client:GetPos() + mainSequence.offset(client))
end
mainDuration = mainSequence.duration
mainSequence = mainSequence[1]
end
local startSequence = data.start and data.start[variant] or ""
local startDuration
if (istable(startSequence)) then
startDuration = startSequence.duration
startSequence = startSequence[1]
end
client:SetNetVar("actEnterAngle", client:GetAngles())
client:ForceSequence(startSequence, function()
-- we've finished the start sequence
client.ixUntimedSequence = data.untimed -- client can exit after the start sequence finishes playing
local duration = client:ForceSequence(mainSequence, function()
-- we've stopped playing the main sequence (either duration expired or user cancelled the act)
if (data.finish) then
local finishSequence = data.finish[variant]
local finishDuration
if (istable(finishSequence)) then
finishDuration = finishSequence.duration
finishSequence = finishSequence[1]
end
client:ForceSequence(finishSequence, function()
-- client has finished the end sequence and is no longer playing any animations
self:ExitAct(client)
end, finishDuration)
else
-- there's no end sequence so we can exit right away
self:ExitAct(client)
end
end, data.untimed and 0 or (mainDuration or nil))
if (!duration) then
-- the model doesn't support this variant
self:ExitAct(client)
client:NotifyLocalized("modelNoSeq")
return
end
end, startDuration, nil)
net.Start("ixActEnter")
net.WriteBool(data.idle or false)
net.Send(client)
client.ixNextAct = CurTime() + 4
end
ix.command.Add("Act" .. act, COMMAND)
end
-- setup exit act command
local COMMAND = {
privilege = "Player Acts",
OnRun = function(command, client)
if (client.ixUntimedSequence) then
client:LeaveSequence()
end
end
}
if (CLIENT) then
-- hide this command from the command list
COMMAND.OnCheckAccess = function(client)
return false
end
end
ix.command.Add("ExitAct", COMMAND)
end
function PLUGIN:UpdateAnimation(client, moveData)
local angle = client:GetNetVar("actEnterAngle")
if (angle) then
client:SetRenderAngles(angle)
end
end
do
local keyBlacklist = IN_ATTACK + IN_ATTACK2
function PLUGIN:StartCommand(client, command)
if (client:GetNetVar("actEnterAngle")) then
command:RemoveKey(keyBlacklist)
end
end
end

View File

@@ -0,0 +1,52 @@
local PLUGIN = PLUGIN
util.AddNetworkString("ixActEnter")
util.AddNetworkString("ixActLeave")
function PLUGIN:CanPlayerEnterAct(client, modelClass, variant, act)
if (!client:Alive() or client:GetLocalVar("ragdoll") or client:WaterLevel() > 0 or !client:IsOnGround()) then
return false, L("notNow", client)
end
-- check if player's model class has an entry in this act table
modelClass = modelClass or ix.anim.GetModelClass(client:GetModel())
local data = act[modelClass]
if (!data) then
return false, L("modelNoSeq", client)
end
-- some models don't support certain variants
local sequence = data.sequence[variant]
if (!sequence) then
return false, L("modelNoSeq", client)
end
return true
end
function PLUGIN:PlayerDeath(client)
if (client.ixUntimedSequence) then
client:SetNetVar("actEnterAngle")
client:LeaveSequence()
client.ixUntimedSequence = nil
end
end
function PLUGIN:PlayerSpawn(client)
if (client.ixUntimedSequence) then
client:SetNetVar("actEnterAngle")
client:LeaveSequence()
client.ixUntimedSequence = nil
end
end
function PLUGIN:OnCharacterFallover(client)
if (client.ixUntimedSequence) then
client:SetNetVar("actEnterAngle")
client:LeaveSequence()
client.ixUntimedSequence = nil
end
end