This commit is contained in:
lifestorm
2024-08-04 23:54:45 +03:00
parent 8064ba84d8
commit 6a58f406b1
7522 changed files with 4011896 additions and 15 deletions

View File

@@ -0,0 +1,250 @@
--[[
| 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.weapons = ix.weapons or {}
ix.weapons.hitBoxList = ix.weapons.hitBoxList or {}
ix.weapons.hitBoxDamageList = ix.weapons.hitBoxDamageList or {}
ix.weapons.weaponCatList = ix.weapons.weaponCatList or {}
ix.weapons.weaponList = ix.weapons.weaponList or {}
ix.weapons.weaponAimPenalty = ix.weapons.weaponAimPenalty or {}
ix.weapons.weaponDamage = ix.weapons.weaponDamage or {}
ix.weapons.weaponArmorPen = ix.weapons.weaponArmorPen or {}
ix.weapons.weaponNumShots = ix.weapons.weaponNumShots or {}
ix.weapons.armorHitBox = {
[HITGROUP_CHEST] = true,
[HITGROUP_STOMACH] = true,
}
ix.weapons.effRange = {
min = 0,
max = 50,
minVal = 50,
maxVal = 200
}
ix.weapons.maxRange = {
min = 0,
max = 50,
minVal = 200,
maxVal = 500
}
function ix.weapons:RegisterWeaponCat(uid, data)
self.weaponCatList[uid] = {
armorPen = math.Clamp(data.armorPen, 0, 1),
baseDamage = data.baseDamage,
numShots = data.numShots,
name = data.name or uid,
gunSkill = {
{inVal = 0, outVal = math.Clamp(data.noSkillMod, 0, 1)},
{inVal = data.minSkill, outVal = 1},
{inVal = data.maxSkill, outVal = math.Clamp(data.maxSkillMod, 1, 1.2)}
},
effRange = {
{inVal = 0, outVal = math.Clamp(data.pointBlankMod, 0, 1)},
{inVal = data.minEffRange, outVal = 1},
{inVal = data.effRange, outVal = 1},
{inVal = data.maxEffRange, outVal = 0.2}
},
aimPenalty = data.aimPenalty or 1
}
end
function ix.weapons:RegisterWeapon(weapon, cat)
self.weaponList[weapon] = cat
end
function ix.weapons:RegisterHitBox(hitBox, minRange, minRangeVal, maxRange, maxRangeVal, critAdjust, hitAdjust)
self.hitBoxList[hitBox] = {
min = minRange,
minVal = math.Clamp(minRangeVal, 0, 0.7),
max = maxRange,
maxVal = math.Clamp(maxRangeVal, 0, 0.7)
}
self.hitBoxDamageList[hitBox] = {
crit = critAdjust,
hit = hitAdjust
}
end
function ix.weapons:RegisterWeaponExceptions(weapon, damage, armorPen, aimPenalty, numShots)
if (damage) then
self.weaponDamage[weapon] = damage --also used for melee base damage adjust
end
if (armorPen) then
self.weaponArmorPen[weapon] = armorPen --also used for melee armor hit adjust
end
if (aimPenalty) then
self.weaponAimPenalty[weapon] = aimPenalty --also used for melee base hit adjust
end
if (numShots) then
self.weaponNumShots[weapon] = numShots
end
end
function ix.weapons:RemapMulti(value, dataTbl)
if (value < dataTbl[1].inVal) then
return dataTbl[1].outVal
elseif (value >= dataTbl[#dataTbl].inVal) then
return dataTbl[#dataTbl].outVal
else
for k, v in ipairs(dataTbl) do
if (value >= v.inVal and value < dataTbl[k + 1].inVal) then
return math.Remap(value, v.inVal, dataTbl[k + 1].inVal, v.outVal, dataTbl[k + 1].outVal)
end
end
end
end
function ix.weapons:RemapClamp(value, dataTbl)
if (value < dataTbl.min) then
return dataTbl.minVal
elseif (value > dataTbl.max) then
return dataTbl.maxVal
else
return math.Remap(value, dataTbl.min, dataTbl.max, dataTbl.minVal, dataTbl.maxVal)
end
end
function ix.weapons:GetArmorPen(weaponClass)
if (self.weaponArmorPen[weaponClass]) then
return self.weaponArmorPen[weaponClass]
end
if (!self.weaponList[weaponClass]) then
return 1
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 1
end
return self.weaponCatList[weaponCat].armorPen
end
function ix.weapons:GetWeaponSkillRequired(weaponClass)
if (!self.weaponList[weaponClass]) then
return 0, 50
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 0, 50
end
return self.weaponCatList[weaponCat].gunSkill[2].inVal, self.weaponCatList[weaponCat].gunSkill[3].inVal, self.weaponCatList[weaponCat].gunSkill[3].outval == 1
end
function ix.weapons:GetWeaponSkillMod(character, weaponClass, bOnlyCat)
if (!bOnlyCat and !self.weaponList[weaponClass]) then
return 1
end
local weaponCat = (bOnlyCat and weaponClass) or self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 1
end
return self:RemapMulti(math.max(character:GetSkillLevel("guns"), 0),
self.weaponCatList[weaponCat].gunSkill)
end
function ix.weapons:GetRangeSkillMod(character, range)
local gunSkill = character:GetSkillLevel("guns")
local minEff, maxEff = self:RemapClamp(gunSkill, self.effRange), self:RemapClamp(gunSkill, self.maxRange)
return math.min(1.2, math.Remap(range, minEff, maxEff, 1.2, 1))
end
function ix.weapons:GetWeaponEffectiveRanges(weaponClass)
if (!self.weaponList[weaponClass]) then
return 1
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 1
end
-- return min effective, max effective, max range
return self.weaponCatList[weaponCat].effRange[2].inVal, self.weaponCatList[weaponCat].effRange[3].inVal, self.weaponCatList[weaponCat].effRange[4].inVal
end
function ix.weapons:GetWeaponEffRangeMod(weaponClass, range)
if (!self.weaponList[weaponClass]) then
return 1
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 1
end
return self:RemapMulti(range, self.weaponCatList[weaponCat].effRange)
end
function ix.weapons:GetWeaponAimPenalty(weaponClass)
if (self.weaponAimPenalty[weaponClass]) then
return self.weaponAimPenalty[weaponClass]
end
if (!self.weaponList[weaponClass]) then
return 1
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 1
end
return self.weaponCatList[weaponCat].aimPenalty
end
function ix.weapons:GetWeaponBaseDamage(weaponClass)
if (self.weaponDamage[weaponClass]) then
return self.weaponDamage[weaponClass]
end
if (!self.weaponList[weaponClass]) then
return
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return
end
return self.weaponCatList[weaponCat].baseDamage
end
function ix.weapons:GetWeaponNumShots(weaponClass)
if (self.weaponNumShots[weaponClass]) then
return self.weaponNumShots[weaponClass]
end
if (!self.weaponList[weaponClass]) then
return 1
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 1
end
return self.weaponCatList[weaponCat].numShots
end

View File

@@ -0,0 +1,113 @@
--[[
| 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.weapons = ix.weapons or {}
ix.weapons.meleeWeapons = ix.weapons.meleeWeapons or {}
ix.weapons.meleeCat = ix.weapons.meleeCat or {}
ix.weapons.bluntWeapons = ix.weapons.bluntWeapons or {}
function ix.weapons:RegisterMeleeWeapon(weapon, category)
ix.weapons.meleeWeapons[weapon] = category
end
function ix.weapons:IsMelee(weapon)
return ix.weapons.meleeWeapons[weapon]
end
function ix.weapons:RegisterMeleeCategory(uid, baseDamage, hitScale, critSkillScale, armorPen)
self.meleeCat[uid] = {
armorPen = armorPen,
baseDamage = baseDamage,
critSkillScale = critSkillScale,
hitScale = hitScale,
}
end
function ix.weapons:GetMeleeWeaponBaseHitChance(character, weaponClass)
if (self.weaponAimPenalty[weaponClass]) then
return self.weaponAimPenalty[weaponClass]
end
if (!self.meleeWeapons[weaponClass]) then
return 0.8
end
local weaponCat = self.meleeWeapons[weaponClass]
if (!self.meleeCat[weaponCat]) then
return 0.8
end
return character:GetSkillScale(self.meleeCat[weaponCat].critSkillScale) or 0.8
end
function ix.weapons:GetMeleeArmorPen(weaponClass)
if (self.weaponArmorPen[weaponClass]) then
return self.weaponArmorPen[weaponClass]
end
if (self.bluntWeapons[weaponClass]) then
return 1
end
local weaponCat = self.meleeWeapons[weaponClass]
if (!self.meleeCat[weaponCat]) then
return 1
end
return self.meleeCat[weaponCat].armorPen
end
function ix.weapons:GetMeleeWeaponBaseDamage(weaponClass, attackType)
local scale = 1
if (attackType == "heavy") then
scale = 2.5
elseif (attackType == "bash") then
scale = 0.7
end
if (self.weaponDamage[weaponClass]) then
return self.weaponDamage[weaponClass] * scale
end
if (!self.meleeWeapons[weaponClass]) then
return 16 * scale
end
local weaponCat = self.meleeWeapons[weaponClass]
if (!self.meleeCat[weaponCat]) then
return 16 * scale
end
return self.meleeCat[weaponCat].baseDamage * scale
end
function ix.weapons:GetMeleeWeaponHitAdjsut(weaponClass)
if (!self.meleeWeapons[weaponClass]) then
return 0.8
end
local weaponCat = self.meleeWeapons[weaponClass]
if (!self.meleeCat[weaponCat]) then
return 0.8
end
return self.meleeCat[weaponCat].hitScale
end
ix.weapons:RegisterMeleeCategory("light", 24, 0.8, "melee_light", 0.4)
ix.weapons:RegisterMeleeCategory("medium", 36, 0.5, "melee_medium", 0.6)
ix.weapons:RegisterMeleeCategory("heavy", 42, 0.4, "melee_heavy", 0.8)
ix.weapons:RegisterMeleeWeapon("ix_stunstick", "light")
--ix.weapons:RegisterMeleeWeapon("tfa_nmrih_kknife", "light")

View File

@@ -0,0 +1,344 @@
--[[
| 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/
--]]
PLUGIN.name = "Weapon Balance"
PLUGIN.author = "Gr4Ss & M!NT"
PLUGIN.description = "Changes the way damage is applied and allows weapons their damage to be rebalanced to be more RP appropriate."
ix.weapons = ix.weapons or {}
ix.weapons.messageTypes = {
["WEAPON_DAMAGE"] = 1,
["ARMOR_DAMAGE"] = 2,
["ARMOR_HALF_DAMAGE"] = 3,
["ARMOR_BREAK"] = 4
}
ix.config.Add("weaponBalanceDebugOutput", false, "Print output parameters for weapon balance.", nil, {
category = "Weapon Balance"
})
ix.config.Add("weaponBalanceDebugRolls", false, "Print rolls results for weapon balance.", nil, {
category = "Weapon Balance"
})
ix.config.Add("corpseMax", 8, "Maximum number of corpses that are allowed to be spawned.", nil, {
data = {min = 0, max = 20},
category = "Persistent Corpses"
})
ix.config.Add("npcArmorPenetration", 0.5, "Percentage of NPC damage which penetrates armor.", nil, {
data = {min = 0, max = 1, decimals = 2},
category = "Weapon Balance"
})
ix.config.Add("defaultArmorPenetration", 0.4, "Percentage of damage which penetrates armor when not specified by the item's configuration.", nil, {
data = {min = 0, max = 1, decimals = 2},
category = "Weapon Balance"
})
ix.config.Add("corpseDecayTime", 60, "How long it takes for a corpse to decay in seconds. Set to 0 to never decay.", nil, {
data = {min = 0, max = 1800},
category = "Persistent Corpses"
})
ix.option.Add("showAdditionalDamageInfo", ix.type.bool, false, {
category = "notice"
})
ix.option.Add("showDamageInfoChat", ix.type.bool, true, {
category = "notice"
})
ix.lang.AddTable("english", {
optShowAdditionalDamageInfo = "Ekstra Silah Hasarı Bilgisini Göster",
optdShowAdditionalDamageInfo = "Silah hasar sonucunuz hakkında ekstra bilgi gösterir: kritik şansınız, cezalarınız ve alınan bonuslar. Uyarı: sohbet-spam!",
optShowDamageInfoChat = "Sohbette Hasar Bilgilerini Göster",
optdShowDamageInfoChat = "Hasar bilgisini sohbette gösterir. Devre dışı bırakılırsa, hasar bilgisi bunun yerine konsola yazdırılır."
})
ix.lang.AddTable("spanish", {
optShowAdditionalDamageInfo = "Enseña más Información sobre el Daño del Arma",
optdShowDamageInfoChat = "Muestra la información sobre el daño en el chat. Si se desactiva, la información sobre el daño aparecerá en la consola.",
optShowDamageInfoChat = "Muestra Información de Daño en el chat",
optdShowAdditionalDamageInfo = "Muestra información extra sobre el resultado de daño de un arma: Tu posibilidad de crítico, penalizaciones y bonus. Cuidado: ¡Chat spam!"
})
do
local CLASS = {}
local iconOrange = ix.util.GetMaterial("willardnetworks/chat/gun_orange.png")
local iconRed = ix.util.GetMaterial("willardnetworks/chat/gun_red.png")
local orange = Color(255, 144, 0)
local orangeAlt = Color(255, 171, 62)
local orangeAlt2 = Color(255, 197, 122)
local red = Color(217, 83, 83)
local redAlt = Color(150, 83, 83)
if (CLIENT) then
function CLASS:OnChatAdd(speaker, text, anonymous, data)
if (!data.messageType) then return end
local icon = data.bCrit and iconRed or iconOrange
local color = orange
local critText = data.bCrit and "critically hit " or "hit "
local targetText = ((!IsValid(data.target) or data.target:IsPlayer()) and "your target") or data.target:GetClass()
local hitBoxText = (data.hitBox and " in the "..data.hitBox) or ""
local range = data.range == -1 and "" or " at "..data.range.." meters"
local suffix = ""
if (data.messageType == ix.weapons.messageTypes.WEAPON_DAMAGE) then
color = data.bCrit and red or orange
suffix = ", dealing "..data.damage.." damage"
elseif (data.messageType == ix.weapons.messageTypes.ARMOR_DAMAGE) then
color = data.bCrit and orangeAlt or orangeAlt2
suffix = ", dealing "..data.damage.." damage to their armor"
elseif (data.messageType == ix.weapons.messageTypes.ARMOR_HALF_DAMAGE) then
color = data.bCrit and orangeAlt or orangeAlt2
suffix = ", dealing "..data.damage.." damage, and dealing "..math.Round(data.damage / 2).." damage to their armor"
elseif (data.messageType == ix.weapons.messageTypes.ARMOR_BREAK) then
color = data.bCrit and red or redAlt
suffix = ", breaking their armor"
end
local punctuation = data.bCrit and "!" or "."
local finalText1 = table.concat({
"You ", critText, targetText, range, hitBoxText, suffix, punctuation
})
if (ix.option.Get("showDamageInfoChat")) then
chat.AddText(icon, color, finalText1)
else
MsgC(color, finalText1, "\n")
end
if (ix.option.Get("showAdditionalDamageInfo")) then
local penalties, bonus = {}, {}
if (bit.band(data.penalties, 1) == 1) then penalties[#penalties + 1] = "bodypart hit range" end
if (bit.band(data.penalties, 2) == 2) then penalties[#penalties + 1] = "target armor" end
if (bit.band(data.penalties, 4) == 4) then penalties[#penalties + 1] = "gun skill" end
if (bit.band(data.penalties, 8) == 8) then penalties[#penalties + 1] = "skill effective range" end
if (bit.band(data.penalties, 16) == 16) then penalties[#penalties + 1] = "gun effective range" end
if (bit.band(data.penalties, 32) == 32) then penalties[#penalties + 1] = "gun aim penalty" end
if (bit.band(data.penalties, 64) == 64) then penalties[#penalties + 1] = "target speed/movement" end
if (bit.band(data.penalties, 128) == 128) then penalties[#penalties + 1] = "melee skill" end
if (bit.band(data.bonus, 1) == 1) then bonus[#bonus + 1] = "bodypart hit range" end
if (bit.band(data.bonus, 4) == 4) then bonus[#bonus + 1] = "gun skill" end
if (bit.band(data.bonus, 8) == 8) then bonus[#bonus + 1] = "skill effective range" end
if (bit.band(data.bonus, 128) == 128) then bonus[#bonus + 1] = "melee skill" end
local finalText2 = table.concat({
"Final crit chance: ", math.Clamp(math.Round(data.calcCrit * 100), 0, 100), "%",
" | Penalties: ", (#penalties > 0 and table.concat(penalties, ", ")) or "none",
" | Bonus: ", (#bonus > 0 and table.concat(bonus, ", ")) or "none"
}, "")
if (ix.option.Get("showDamageInfoChat")) then
chat.AddText(icon, color, finalText2)
else
MsgC(color, finalText2, "\n")
end
end
end
end
ix.chat.Register("weapon_damage", CLASS)
end
ix.util.Include("sv_hooks.lua")
ix.util.Include("sv_util.lua")
ix.util.Include("sv_plugin.lua")
--PLUGIN:RegisterHitBox(hitBox, minRange, minRangeVal, maxRange, maxRangeVal, critAdjust, hitAdjust)
-- How to Modify damage, plz help. I was told the below does, but it doesn't affect the damage modifiers!!! @Gr4Ss
-- ^^ nvm I fix it :) - M!NT
ix.weapons:RegisterHitBox(HITGROUP_HEAD, 40, 0.1, 85, 0.05, 1.25, 1.0)
-- CENTER OF MASS
ix.weapons:RegisterHitBox(HITGROUP_CHEST, 40, 0.3, 85, 0.2, 1.1, 1.0)
ix.weapons:RegisterHitBox(HITGROUP_STOMACH, 40, 0.4, 85, 0.3, 1.1, 1.0)
ix.weapons:RegisterHitBox(HITGROUP_GENERIC, 40, 0.5, 85, 0.4, 1.1, 0.9)
ix.weapons:RegisterHitBox(HITGROUP_GEAR, 40, 0.5, 85, 0.4, 0.9, 0.7)
-- ARM
ix.weapons:RegisterHitBox(HITGROUP_LEFTARM, 40, 0.3, 85, 0.2, 0.9, 0.7)
ix.weapons:RegisterHitBox(HITGROUP_RIGHTARM, 40, 0.3, 85, 0.2, 0.9, 0.7)
-- LEG
ix.weapons:RegisterHitBox(HITGROUP_LEFTLEG, 40, 0.3, 85, 0.2, 0.9, 0.7)
ix.weapons:RegisterHitBox(HITGROUP_RIGHTLEG, 40, 0.3, 85, 0.2, 0.9, 0.7)
ix.weapons:RegisterWeaponCat("pistol", {
name = "Pistol",
baseDamage = 9,
armorPen = 0.2,
numShots = 3,
noSkillMod = 0.8,
minSkill = 0,
maxSkill = 20,
maxSkillMod = 1.1,
pointBlankMod = 1,
minEffRange = 0,
effRange = 40,
maxEffRange = 75
})
ix.weapons:RegisterWeaponCat("revolver", {
name = "Revolver",
baseDamage = 28,
armorPen = 0.4,
numShots = 1,
noSkillMod = 0.7,
minSkill = 0,
maxSkill = 30,
maxSkillMod = 1.2,
pointBlankMod = 1,
minEffRange = 0,
effRange = 40,
maxEffRange = 80
})
ix.weapons:RegisterWeaponCat("smg", {
name = "SMG",
baseDamage = 9,
armorPen = 0.4,
numShots = 4,
noSkillMod = 0.7,
minSkill = 10,
maxSkill = 30,
maxSkillMod = 1.2,
pointBlankMod = 1,
minEffRange = 0,
effRange = 50,
maxEffRange = 100
})
ix.weapons:RegisterWeaponCat("assaultrifle", {
name = "Assault Rifle",
baseDamage = 17,
armorPen = 0.4,
numShots = 3,
noSkillMod = 0.5,
minSkill = 20,
maxSkill = 40,
maxSkillMod = 1.2,
pointBlankMod = 0.8,
minEffRange = 1,
effRange = 75,
maxEffRange = 150
})
ix.weapons:RegisterWeaponCat("dmr", {
name = "DMR",
baseDamage = 24,
armorPen = 0.45,
numShots = 2,
noSkillMod = 0.4,
minSkill = 20,
maxSkill = 50,
maxSkillMod = 1.2,
pointBlankMod = 0.6,
minEffRange = 2,
effRange = 125,
maxEffRange = 250
})
ix.weapons:RegisterWeaponCat("boltaction", {
name = "Bolt Action Rifle",
baseDamage = 61,
armorPen = 0.4,
numShots = 1,
noSkillMod = 0.4,
minSkill = 30,
maxSkill = 50,
maxSkillMod = 1.1,
pointBlankMod = 0.4,
minEffRange = 3,
effRange = 300,
maxEffRange = 500
})
ix.weapons:RegisterWeaponCat("shotgun", {
name = "Shotgun",
baseDamage = 11,
armorPen = 0.2,
numShots = 1,
noSkillMod = 0.8,
minSkill = 5,
maxSkill = 20,
maxSkillMod = 1,
pointBlankMod = 1,
minEffRange = 0,
effRange = 40,
maxEffRange = 80
})
ix.weapons:RegisterWeaponCat("lmg", {
name = "Light Machine Gun",
baseDamage = 10,
armorPen = 0.1,
numShots = 8,
noSkillMod = 0.3,
minSkill = 40,
maxSkill = 50,
maxSkillMod = 1,
pointBlankMod = 0.6,
minEffRange = 1,
effRange = 75,
maxEffRange = 150
})
--[[
hit/critical = (hitBoxCrit(range, hitBox) * armorPen(hitBox == torso/stomach, weaponCat)) * ((weaponAimSkill(gunSkill, weaponCat) * rangeAimSkill(gunSkill, range)) * effectiveRangeMod(range, weaponCat)) * dodge(victimSpeedSkill, isFFMoving, range)
(max 0.7 * (max 1)) * ((max 1.2 * max 1.2) * max 1) * max 1
-> Hit: damage = (damage * hitBoxDamageCritAdjustment(hitBox))
-> Miss: damage = (damage * hitBoxDamageHitAdjustment(hitBox))
data points:
HitBox (for every hitbox):
minRange -- below & up to minRange -> minRangeVal
minRangeVal -- val between 0-0.7
-- in between minRange & maxRange -> interpolate between inRangeVal & maxRangeVal
maxRange -- from maxRange & above -> maxRangeVal
maxRangeVal -- val between 0-0.7
WeaponCat (for every weapon category):
armorPen -- lower crit chance if hitting armor -> between 0-1
noSkillVal -- penalty for zero/no skill -> val between 0-1
-- in between 0 and minAimSkill -> interpolate between noSkillLevel and 1
minAimSkill -- level at which no penalty/bonus is given -> aka val = 1
maxAimSkill -- level at which maximum aimSkill bonus is given
maxSkillVal -- bonus given at/above maxAimSkill, val between 1-1.2
pointBlankMod -- val between 0 and 1
-- in between 0 and MinEffrange -> interpolate between pointBlankMod and 1
MinEffRange
-- in between MinEffRange and EffRange -> mod = 1
EffRange
-- in between EffRange and MaxEffRange -> interpolate between 1 and 0.2
MaxEffRange
GunSkill:
zeroEffRange -- Effective range at level 0
maxEffRange -- Effective range at level 50
-- below effective range = 1.2 bonus
-- between eff range and max range = interpolate between 1.2 and 1
-- above max range = max(interpolate between 1 and .., 0) (same scale as eff range to max range)
zeroMaxRange -- Max Effective range at level 0
maxMaxRange -- Max Effective range at level 50
Weapon (for every weapon):
category
]]
function PLUGIN:TFA_CanBash(weapon)
return false
end
for _, item in pairs(ix.item.list) do
if (item.balanceCat and ix.weapons) then
if (item.isMelee) then
ix.weapons:RegisterMeleeWeapon(item.class, item.balanceCat)
else
ix.weapons:RegisterWeapon(item.class, item.balanceCat)
end
ix.weapons:RegisterWeaponExceptions(item.class, item.baseDamage, item.armorPen, item.aimPenalty, item.numShots)
end
end

View File

@@ -0,0 +1,315 @@
--[[
| 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 PLUGIN = PLUGIN
function PLUGIN:GetHookCallPriority(hook)
if (hook == "EntityTakeDamage") then
return 0
end
end
function PLUGIN:FirefightActionMove(client, character, fightInfo)
local weapon = client:GetActiveWeapon()
if (IsValid(weapon) and ix.weapons:IsMelee(weapon:GetClass())) then
if (fightInfo.turn.actions == 1) then
fightInfo.turn.weapon = weapon:GetClass()
fightInfo.turn.attacksLeft = 1
end
end
end
function PLUGIN:FirefightTurnStart(fight, fightInfo, character)
local client = character:GetPlayer()
if (IsValid(client:GetActiveWeapon()) and ix.weapons:IsMelee(client:GetActiveWeapon():GetClass())) then
fightInfo.turn.attacksLeft = 1
fightInfo.turn.weapon = client:GetActiveWeapon():GetClass()
end
end
function PLUGIN:ScaleNPCDamage(npc, hitgroup, damage)
local attacker = damage:GetAttacker()
if (!IsValid(attacker) or (!attacker:IsPlayer())) then
return
end
local success, result, range, critChance, penalties, bonus = ix.weapons:BalanceDamage(npc, damage, attacker, hitgroup)
if (success == false) then
return
end
ix.weapons:QueueDamageMessage(attacker, {
bCrit = result == "crit",
target = npc,
hitBox = PLUGIN.hitGroupName[hitgroup],
range = math.Round(range),
damage = damage:GetDamage(),
calcCrit = critChance,
penalties = penalties,
bonus = bonus,
messageType = ix.weapons.messageTypes.WEAPON_DAMAGE
})
end
function PLUGIN:EntityTakeDamage(victim, damage)
if (!IsValid(victim)) then return end
-- Ensure we deal damage to the player if their ragdoll is hit
local bIsRagdoll = false
if (victim:GetClass() == "prop_ragdoll" and IsValid(victim.ixPlayer)) then
if (IsValid(victim.ixHeldOwner)) then
damage:SetDamage(0)
return true
end
if (damage:IsDamageType(DMG_CRUSH)) then
if ((victim.ixFallGrace or 0) < CurTime()) then
if (damage:GetDamage() <= 10) then
damage:SetDamage(0)
end
victim.ixFallGrace = CurTime() + 0.5
else
return
end
end
bIsRagdoll = victim
victim = victim.ixPlayer
end
-- Not attacking player
if (!victim:IsPlayer()) then
if (victim:IsNPC()) then return end -- gets handled by ScaleNPCDamage
--local attacker = damage:GetAttacker()
--if (!IsValid(attacker) or (!attacker:IsPlayer())) then
-- return
--end
--return ix.weapons:BalanceDamage(victim, damage, attacker, 0)
return --I dunno. All I know is that regular entities take damage again with the above commented out
end
if (!victim:Alive()) then return end
-- Is attacking player
local attacker = damage:GetAttacker()
local hitgroup = !bIsRagdoll and victim:LastHitGroup() or self:CalculateRagdollHitGroup(victim, damage:GetDamagePosition())
-- Do schema damage hook if it exists
if (Schema.EntityTakeDamage and Schema:EntityTakeDamage(victim, damage)) then
return true
end
-- Do GM & addon damage hooks
if (hook.ixCall("EntityTakeDamage", Schema.GM, victim, damage)) then
return true
end
-- Check if we are dealing invalid damage to a ragdoll
if (bIsRagdoll and IsValid(attacker) and !attacker:IsPlayer() and (attacker:GetClass() == "prop_ragdoll" or attacker:IsDoor() or damage:GetDamage() < 5)) then
damage:SetDamage(0)
return true
end
-- Allow for schema damage overrides
if (hook.Run("CalculatePlayerDamage", victim, damage, hitgroup)) then
return true
end
-- Do gunbalance damage limitations
local success, result, range, critChance, penalties, bonus
if (IsValid(attacker) and attacker:IsPlayer()) then
success, result, range, critChance, penalties, bonus = ix.weapons:BalanceDamage(victim, damage, attacker, hitgroup)
if (success == false or !range or !tonumber(range)) then
-- we don't know what to do with this damage
return
end
end
-- If still dealing damage
if (damage:GetDamage() > 0) then
local messageType
local finalDamage
local weaponItem
if (IsValid(attacker) and attacker:IsPlayer()) then
weaponItem = attacker:GetActiveWeapon().ixItem
end
local armorPen = weaponItem and weaponItem.armorPen or ix.config.Get("defaultArmorPenetration", 0.5)
if (IsValid(attacker) and attacker:IsNPC()) then
armorPen = ix.config.Get("npcArmorPen", 0.5)
end
if (victim:Armor() > 0) then
if (damage:IsBulletDamage() or damage:IsDamageType(DMG_CLUB) or damage:IsDamageType(DMG_SLASH)) then
local armor = victim:Armor() - (damage:GetDamage() * armorPen)
if (armor < 0) then
finalDamage = damage:GetDamage() * armorPen
messageType = ix.weapons.messageTypes.ARMOR_BROKEN
victim:SetHealth(math.max(victim:Health() - math.abs(armor), 1))
victim:SetArmor(0)
else
finalDamage = damage:GetDamage() * armorPen
messageType = ix.weapons.messageTypes.ARMOR_DAMAGE
victim:SetArmor(armor)
end
elseif (damage:IsDamageType(DMG_SHOCK)) then
finalDamage = damage:GetDamage()
messageType = ix.weapons.messageTypes.ARMOR_HALF_DAMAGE
victim:SetArmor(math.max(victim:Armor() - finalDamage / 2, 0))
victim:SetHealth(math.max(victim:Health() - damage:GetDamage(), 1))
else
finalDamage = damage:GetDamage()
messageType = ix.weapons.messageTypes.WEAPON_DAMAGE
victim:SetHealth(math.max(victim:Health() - damage:GetDamage(), 1))
end
elseif victim:GetCharacter():GetFaction() == FACTION_VORT then
finalDamage = damage:GetDamage() * armorPen
messageType = ix.weapons.messageTypes.WEAPON_DAMAGE
victim:SetHealth(math.max(victim:Health() - finalDamage, 1))
else
finalDamage = damage:GetDamage()
messageType = ix.weapons.messageTypes.WEAPON_DAMAGE
victim:SetHealth(math.max(victim:Health() - damage:GetDamage(), 1))
end
if (IsValid(attacker) and attacker:IsPlayer() and range) then
ix.weapons:QueueDamageMessage(attacker, {
bCrit = result == "crit",
target = victim,
hitBox = PLUGIN.hitGroupName[hitgroup],
range = math.Round(range),
damage = finalDamage,
calcCrit = critChance,
penalties = penalties,
bonus = bonus,
messageType = messageType
})
end
else
return true
end
-- If Victim is dying
if (victim:Health() == 1) then
if (hook.Run("HandlePlayerDeath", victim, damage) != false) then
self:HandlePlayerDeathFallback(victim, damage)
end
else
hook.Run("PlayerHurt", victim, attacker, victim:Health(), damage:GetDamage())
end
-- Stop further EntityTakeDamage stuff (we already called this!)
damage:SetDamage(0)
return true
end
function PLUGIN:TFA_PreCanPrimaryAttack(weapon)
local client = weapon.Owner
if (!IsValid(client) or !client:IsPlayer()) then return end
client.ixCanDoDamage = weapon.Primary.NumShots
end
function PLUGIN:TFA_PostPrimaryAttack(weapon)
local client = weapon.Owner
if (client:IsNPC()) then return end
local min, max = ix.weapons:GetWeaponSkillRequired(weapon:GetClass())
local level = client:GetCharacter():GetSkill("guns")
if (min <= level and level <= max) then
client:GetCharacter():DoAction("guns_level_approp")
else
client:GetCharacter():DoAction("guns_level_unapprop")
end
end
function PLUGIN:ShouldSpawnClientRagdoll(client)
return false
end
function PLUGIN:PlayerSpawn(client)
client:SetLocalVar("ragdoll", nil)
end
function PLUGIN:ShouldRemoveRagdollOnDeath(client)
return false
end
function PLUGIN:PlayerInitialSpawn(client)
self:CleanupCorpses()
end
function PLUGIN:DoPlayerDeath(client)
-- remove old corpse if we've hit the limit
local maxCorpses = ix.config.Get("corpseMax", 8)
if (maxCorpses > 0) then
local entity = IsValid(client.ixRagdoll) and client.ixRagdoll or client:CreateServerRagdoll()
local decayTime = ix.config.Get("corpseDecayTime", 60)
local uniqueID = "ixCorpseDecay" .. entity:EntIndex()
entity:RemoveCallOnRemove("fixer")
entity:CallOnRemove("ixPersistentCorpse", function(ragdoll)
if (ragdoll.ixInventory) then
ix.storage.Close(ragdoll.ixInventory)
end
if (IsValid(client) and !client:Alive()) then
client:SetLocalVar("ragdoll", nil)
end
local index
for k, v in ipairs(PLUGIN.corpses) do
if (v == ragdoll) then
index = k
break
end
end
if (index) then
table.remove(PLUGIN.corpses, index)
end
if (timer.Exists(uniqueID)) then
timer.Remove(uniqueID)
end
end)
if (decayTime > 0) then
timer.Create(uniqueID, decayTime, 1, function()
if (IsValid(entity)) then
entity:Remove()
else
timer.Remove(uniqueID)
end
end)
end
client.ixRagdoll = nil
entity.ixPlayer = nil
self.corpses[#self.corpses + 1] = entity
if (#self.corpses >= maxCorpses) then
self:CleanupCorpses(maxCorpses)
end
hook.Run("OnPlayerCorpseCreated", client, entity)
end
end

View File

@@ -0,0 +1,189 @@
--[[
| 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 PLUGIN = PLUGIN
function ix.weapons:CalcCritChance(attackerChar, victim, range, hitBox, weaponClass)
local attacker = attackerChar:GetPlayer()
if (!self.hitBoxList[hitBox]) then
return 0, 0, 0
end
local weaponCat = self.weaponList[weaponClass]
if (!self.weaponCatList[weaponCat]) then
return 0, 0, 0
end
local hitBoxCrit = self:RemapClamp(range, self.hitBoxList[hitBox])
local armorMod = (victim:IsPlayer() and victim:Armor() > 0 and self:GetArmorPen(weaponClass)) or 1
local weaponSkillMod = self:GetWeaponSkillMod(attackerChar, weaponClass)
local rangeSkillMod = self:GetRangeSkillMod(attackerChar, range)
local effRangeMod = self:GetWeaponEffRangeMod(weaponClass, range)
local aimPenalty = self:GetWeaponAimPenalty(weaponClass)
local dodgeMod = 1
if (victim:IsPlayer() and ix.fights and victim:GetFight() and victim:GetCharacter().fightInfoTable) then
dodgeMod = (1 - 0.03 *
(attackerChar:GetSkillScale("guns_moving_target_penalty")) * (victim:GetCharacter().fightInfoTable.movePointsSpend or 0) *
(1 + victim:GetCharacter():GetSkillScale("speed_dodge_mod")))
end
ix.log.Add(attacker, "weaponBalanceDebugOutput", hitBoxCrit, armorMod, weaponSkillMod, rangeSkillMod, effRangeMod, dodgeMod)
local penalties, bonus = 0, 0
if (hitBoxCrit < 0.60) then penalties = bit.bor(penalties, 1) elseif (hitBoxCrit >= 0.65) then bonus = bit.bor(bonus, 1) end
if (armorMod < 0.85) then penalties = bit.bor(penalties, 2) end
if (weaponSkillMod < 1) then penalties = bit.bor(penalties, 4) elseif (weaponSkillMod >= 1.1) then bonus = bit.bor(bonus, 4) end
if (rangeSkillMod < 1) then penalties = bit.bor(penalties, 8) elseif (rangeSkillMod >= 1.1) then bonus = bit.bor(bonus, 8) end
if (effRangeMod < 0.85) then penalties = bit.bor(penalties, 16) end
if (aimPenalty < 1) then penalties = bit.bor(penalties, 32) end
if (dodgeMod < 0.85) then penalties = bit.bor(penalties, 64) end
return hitBoxCrit * armorMod * weaponSkillMod * rangeSkillMod * effRangeMod * aimPenalty * dodgeMod, penalties, bonus
end
function ix.weapons:CalcMeleeCritChance(attackerChar, victim, weaponClass)
local attacker = attackerChar:GetPlayer()
local baseCrit = self:GetMeleeWeaponBaseHitChance(attackerChar, weaponClass)
local armorMod = (victim:IsPlayer() and victim:Armor() > 0 and self:GetMeleeArmorPen(weaponClass)) or 1
local dodgeMod = 1
if (victim:IsPlayer() and ix.fights and victim:GetFight() and victim:GetCharacter().fightInfoTable) then
local movePoints = victim:GetCharacter().fightInfoTable.movePointsSpend or 0
if (movePoints >= 8) then
dodgeMod = 0.5
elseif (movePoints >= 4) then
dodgeMod = 0.75
end
end
ix.log.Add(attacker, "weaponMeleeBalanceDebugOutput", baseCrit, armorMod, dodgeMod)
local penalties, bonus = 0, 0
if (baseCrit < 0.1) then penalties = bit.bor(penalties, 128) elseif (baseCrit >= 0.3) then bonus = bit.bor(bonus, 128) end
if (armorMod < 0.85) then penalties = bit.bor(penalties, 2) end
if (dodgeMod < 0.85) then penalties = bit.bor(penalties, 64) end
return baseCrit * armorMod * dodgeMod, penalties, bonus
end
function ix.weapons:BalanceDamage(victim, damage, attacker, hitgroup)
local inflictor = damage:GetInflictor()
if (!IsValid(inflictor)) then return end
if (inflictor.IsTFAWeapon and !inflictor.noDoubleDamageFix) then
if (!attacker.ixCanDoDamage) then
damage:SetDamage(0)
return false
else
attacker.ixCanDoDamage = attacker.ixCanDoDamage - 1
if (attacker.ixCanDoDamage == 0) then
attacker.ixCanDoDamage = nil
end
end
end
if (inflictor:IsPlayer()) then
inflictor = inflictor:GetActiveWeapon()
end
local weaponClass = inflictor:GetClass()
if (IsValid(attacker:GetActiveWeapon()) and self:IsMelee(attacker:GetActiveWeapon():GetClass())) then
weaponClass = attacker:GetActiveWeapon():GetClass()
end
local range = -1
local critChance, penalties, bonus, finalDamage, result
local critRoll = math.random()
if (!self:IsMelee(weaponClass)) then
range = attacker:GetPos():Distance(victim:GetPos()) * 0.30 / 16
critChance, penalties, bonus = self:CalcCritChance(attacker:GetCharacter(), victim, range, hitgroup, weaponClass)
if (!critChance) then return end
result = critRoll < critChance and "crit" or "hit"
local adjust = self.hitBoxDamageList[hitgroup] and self.hitBoxDamageList[hitgroup][result] or 0.9
finalDamage = math.floor((self:GetWeaponBaseDamage(weaponClass) or damage:GetDamage()) * adjust)
if (victim:IsNPC() or victim:IsPlayer()) then
if (result == "hit") then
attacker:GetCharacter():DoAction("guns_hit")
else
attacker:GetCharacter():DoAction("guns_crit")
end
end
else
critChance, penalties, bonus = self:CalcMeleeCritChance(attacker:GetCharacter(), victim, weaponClass)
if (!critChance) then return end
result = critRoll < critChance and "crit" or "hit"
finalDamage = math.floor(self:GetMeleeWeaponBaseDamage(weaponClass, attacker:GetActiveWeapon().ixAttackType) * (result == "crit" and 1 or self:GetMeleeWeaponHitAdjsut(weaponClass)))
if (victim:IsNPC() or victim:IsPlayer()) then
if (result == "hit") then
attacker:GetCharacter():DoAction("melee_hit")
else
attacker:GetCharacter():DoAction("melee_crit")
end
end
end
ix.log.Add(attacker, "weaponBalanceDebugRolls", critChance, critRoll, string.utf8upper(result))
damage:SetDamage(finalDamage)
hook.Run("PostCalculatePlayerDamage", victim, damage, hitgroup)
if (victim:IsNPC() or victim:IsPlayer()) then
ix.log.Add(attacker, "weaponBalanceResult", result, victim, hitgroup, math.Round(damage:GetDamage()), critChance)
end
return true, result, range, critChance, penalties, bonus
end
function ix.weapons:QueueDamageMessage(attacker, data)
if (!self.dmgMessageQueue or !istable(self.dmgMessageQueue)) then
self.dmgMessageQueue = {}
end
local idx = tonumber(attacker:SteamID64())
if (!self.dmgMessageQueue[idx]) then
self.dmgMessageQueue[idx] = {}
self.dmgMessageQueue[idx].lastIndex = 0
self.dmgMessageQueue[idx].ply = attacker
self.dmgMessageQueue[idx].stack = {}
end
self.dmgMessageQueue[idx].stack[table.Count(self.dmgMessageQueue[idx].stack) + 1] = data
end
timer.Create("ixDamageMessageStackConsumer", 0.25, 0, function()
if (!ix.weapons.dmgMessageQueue or !istable(ix.weapons.dmgMessageQueue)) then return end
for i, queue in pairs(ix.weapons.dmgMessageQueue) do
if (!queue.stack[queue.lastIndex + 1] or !IsValid(queue.ply)) then
-- queue is drained
ix.weapons.dmgMessageQueue[i] = nil
continue
end
local data = queue.stack[queue.lastIndex + 1]
ix.chat.Send(queue.ply, "weapon_damage", "", false, queue.ply, {
bCrit = data.bCrit,
target = data.target,
hitBox = data.hitBox,
range = data.range,
damage = data.damage,
calcCrit = data.calcCrit,
penalties = data.penalties,
bonus = data.bonus,
messageType = data.messageType
})
queue.lastIndex = queue.lastIndex + 1
end
end)

View File

@@ -0,0 +1,152 @@
--[[
| 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 PLUGIN = PLUGIN
local ix = ix
PLUGIN.corpses = {}
PLUGIN.hitGroupName = {
[HITGROUP_HEAD] = "head",
[HITGROUP_CHEST] = "chest",
[HITGROUP_STOMACH] = "stomach",
[HITGROUP_LEFTARM] = "left arm",
[HITGROUP_RIGHTARM] = "right arm",
[HITGROUP_LEFTLEG] = "left leg",
[HITGROUP_RIGHTLEG] = "right leg",
[HITGROUP_GEAR] = "gear",
}
PLUGIN.HitGroupBonesCache = {
{"ValveBiped.Bip01_R_UpperArm", HITGROUP_RIGHTARM},
{"ValveBiped.Bip01_R_Forearm", HITGROUP_RIGHTARM},
{"ValveBiped.Bip01_L_UpperArm", HITGROUP_LEFTARM},
{"ValveBiped.Bip01_L_Forearm", HITGROUP_LEFTARM},
{"ValveBiped.Bip01_R_Thigh", HITGROUP_RIGHTLEG},
{"ValveBiped.Bip01_R_Calf", HITGROUP_RIGHTLEG},
{"ValveBiped.Bip01_R_Foot", HITGROUP_RIGHTLEG},
{"ValveBiped.Bip01_R_Hand", HITGROUP_RIGHTARM},
{"ValveBiped.Bip01_L_Thigh", HITGROUP_LEFTLEG},
{"ValveBiped.Bip01_L_Calf", HITGROUP_LEFTLEG},
{"ValveBiped.Bip01_L_Foot", HITGROUP_LEFTLEG},
{"ValveBiped.Bip01_L_Hand", HITGROUP_LEFTARM},
{"ValveBiped.Bip01_Pelvis", HITGROUP_STOMACH},
{"ValveBiped.Bip01_Spine2", HITGROUP_CHEST},
{"ValveBiped.Bip01_Spine1", HITGROUP_CHEST},
{"ValveBiped.Bip01_Head1", HITGROUP_HEAD},
{"ValveBiped.Bip01_Neck1", HITGROUP_HEAD}
}
function PLUGIN:CalculateRagdollHitGroup(ragdoll, position)
local dist, hitGroup
for _, v in pairs(self.HitGroupBonesCache) do
local bone = ragdoll:LookupBone(v[1]);
if (bone) then
local bonePosition = ragdoll:GetBonePosition(bone)
if (bonePosition) then
local distance = bonePosition:DistToSqr(position)
if (!dist or distance < dist) then
dist = distance
hitGroup = v[2]
end
end
end
end
if (hitGroup) then
return hitGroup
else
return HITGROUP_GENERIC
end
end
ix.log.AddType("playerDeath", function(client)
return -1
end, FLAG_DANGER)
ix.log.AddType("playerDeathNew", function(client, ...)
local arg = {...}
return string.format("%s has killed %s%s.", arg[1], client:Name(), arg[2] and (" with " .. arg[2]) or "")
end, FLAG_DANGER)
ix.log.AddType("weaponBalanceResult", function(attacker, result, victim, hitgroup, finalDamage, critChance)
return string.format("%s has %s %s%s dealing %d damage (CritChance was %.2f).",
attacker:IsPlayer() and attacker:Name() or attacker:GetClass(),
result == "crit" and "critically hit" or "hit",
victim:IsPlayer() and victim:Name() or victim:GetClass(),
(PLUGIN.hitGroupName[hitgroup] and " in the "..PLUGIN.hitGroupName[hitgroup]) or "",
finalDamage,
critChance)
end)
ix.log.AddType("weaponBalanceDebugOutput", function(attacker, hitBoxCrit, armorMod, weaponSkillMod, rangeSkillMod, effRangeMod, dodgeMod)
if (ix.config.Get("weaponBalanceDebugOutput")) then
return string.format("[DEBUG-WEPBAL] baseCrit: %.2f; armorMod: %.2f; weaponSkillMod: %.2f; rangeSkillMod: %.2f; effRangeMod: %.2f; dodgeMod: %.2f",
hitBoxCrit, armorMod, weaponSkillMod, rangeSkillMod, effRangeMod, dodgeMod)
else
return -1
end
end)
ix.log.AddType("weaponMeleeBalanceDebugOutput", function(attacker, baseCrit, armorMod, dodgeMod)
if (ix.config.Get("weaponBalanceDebugOutput")) then
return string.format("[DEBUG-WEPBAL] baseCrit: %.2f; armorMod: %.2f; dodgeMod: %.2f",
baseCrit, armorMod, dodgeMod)
else
return -1
end
end)
ix.log.AddType("weaponBalanceDebugRolls", function(attacker, critChance, critRoll, result)
if (ix.config.Get("weaponBalanceDebugRolls")) then
return string.format("[DEBUG-WEPBAL] Chance: %.3f; Roll: %.3f; Result: %s;",
critChance, critRoll, string.utf8upper(result))
else
return -1
end
end)
--[[
Below code is taken from the Persistent Corpses plugin made by `impulse, available here under the MIT license: https://github.com/nebulouscloud/helix-plugins/blob/master/persistent_corpses.lua
Copyright 2018 - 2020 Igor Radovanovic
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
function PLUGIN:HandlePlayerDeathFallback(victim, damage)
-- Custom death log to show correct killer/weapon
local attacker = damage:GetAttacker()
local weapon = damage:GetInflictor()
ix.log.Add(victim, "playerDeathNew",
attacker:GetName() ~= "" and attacker:GetName() or attacker:GetClass(), IsValid(weapon) and weapon:GetClass())
victim:Kill()
end
function PLUGIN:CleanupCorpses(maxCorpses)
maxCorpses = maxCorpses or ix.config.Get("corpseMax", 8)
local toRemove = {}
if (#self.corpses > maxCorpses) then
for k, v in ipairs(self.corpses) do
if (!IsValid(v)) then
toRemove[#toRemove + 1] = k
elseif (#self.corpses - #toRemove > maxCorpses) then
v:Remove()
toRemove[#toRemove + 1] = k
end
end
end
for k, _ in ipairs(toRemove) do
table.remove(self.corpses, k)
end
end