mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
Upload
This commit is contained in:
250
gamemodes/darkrp/plugins/weaponbalance/libs/sh_guns.lua
Normal file
250
gamemodes/darkrp/plugins/weaponbalance/libs/sh_guns.lua
Normal 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
|
||||
113
gamemodes/darkrp/plugins/weaponbalance/libs/sh_melee.lua
Normal file
113
gamemodes/darkrp/plugins/weaponbalance/libs/sh_melee.lua
Normal 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")
|
||||
351
gamemodes/darkrp/plugins/weaponbalance/sh_plugin.lua
Normal file
351
gamemodes/darkrp/plugins/weaponbalance/sh_plugin.lua
Normal file
@@ -0,0 +1,351 @@
|
||||
--[[
|
||||
| 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, "Résultats des rouleaux d’impression pour la balance des armes.", nil, {
|
||||
category = "Équilibre des armes"
|
||||
})
|
||||
|
||||
ix.config.Add("corpseMax", 8, "Nombre maximum de cadavres pouvant être pondus.", nil, {
|
||||
data = {min = 0, max = 20},
|
||||
category = "Cadavres"
|
||||
})
|
||||
|
||||
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 = "Cadavres"
|
||||
})
|
||||
|
||||
ix.option.Add("showAdditionalDamageInfo", ix.type.bool, false, {
|
||||
category = "Notices"
|
||||
})
|
||||
|
||||
ix.option.Add("showDamageInfoChat", ix.type.bool, true, {
|
||||
category = "Notices"
|
||||
})
|
||||
|
||||
ix.lang.AddTable("english", {
|
||||
optShowAdditionalDamageInfo = "Afficher les renseignements sur les dommages causés par une arme supplémentaire",
|
||||
optdShowAdditionalDamageInfo = "Affiche des informations supplémentaires sur les dégâts de votre arme résultat : votre chance crit, pénalités et bonus reçus. Attention : chat-spam!",
|
||||
optShowDamageInfoChat = "Afficher les renseignements sur les dommages dans le clavardage",
|
||||
optdShowDamageInfoChat = "Affiche les informations sur les dommages dans le clavardage. Si elles sont désactivées, les informations sur les dommages seront imprimées dans la console."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("french", {
|
||||
optShowAdditionalDamageInfo = "Afficher les renseignements sur les dommages causés par une arme supplémentaire",
|
||||
optdShowAdditionalDamageInfo = "Affiche des informations supplémentaires sur les dégâts de votre arme résultat : votre chance crit, pénalités et bonus reçus. Attention : chat-spam!",
|
||||
optShowDamageInfoChat = "Afficher les renseignements sur les dommages dans le clavardage",
|
||||
optdShowDamageInfoChat = "Affiche les informations sur les dommages dans le clavardage. Si elles sont désactivées, les informations sur les dommages seront imprimées dans la console."
|
||||
})
|
||||
|
||||
ix.lang.AddTable("spanish", {
|
||||
optShowAdditionalDamageInfo = "Afficher les renseignements sur les dommages causés par une arme supplémentaire",
|
||||
optdShowAdditionalDamageInfo = "Affiche des informations supplémentaires sur les dégâts de votre arme résultat : votre chance crit, pénalités et bonus reçus. Attention : chat-spam!",
|
||||
optShowDamageInfoChat = "Afficher les renseignements sur les dommages dans le clavardage",
|
||||
optdShowDamageInfoChat = "Affiche les informations sur les dommages dans le clavardage. Si elles sont désactivées, les informations sur les dommages seront imprimées dans la console."
|
||||
})
|
||||
|
||||
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 "coup critique " or "coup "
|
||||
local targetText = ((!IsValid(data.target) or data.target:IsPlayer()) and "votre cible") or data.target:GetClass()
|
||||
local hitBoxText = (data.hitBox and " dans la "..data.hitBox) or ""
|
||||
local range = data.range == -1 and "" or " à "..data.range.." mètres"
|
||||
local suffix = ""
|
||||
if (data.messageType == ix.weapons.messageTypes.WEAPON_DAMAGE) then
|
||||
color = data.bCrit and red or orange
|
||||
suffix = ", infligeant "..data.damage.." dégâts"
|
||||
|
||||
elseif (data.messageType == ix.weapons.messageTypes.ARMOR_DAMAGE) then
|
||||
color = data.bCrit and orangeAlt or orangeAlt2
|
||||
suffix = ", infligeant "..data.damage.." dégats sur l'armure"
|
||||
|
||||
elseif (data.messageType == ix.weapons.messageTypes.ARMOR_HALF_DAMAGE) then
|
||||
color = data.bCrit and orangeAlt or orangeAlt2
|
||||
suffix = ", infligeant "..data.damage.." dégats, et inflige "..math.Round(data.damage / 2).." dégats sur l'armure"
|
||||
|
||||
elseif (data.messageType == ix.weapons.messageTypes.ARMOR_BREAK) then
|
||||
color = data.bCrit and red or redAlt
|
||||
suffix = ", détruit l'armure"
|
||||
end
|
||||
|
||||
local punctuation = data.bCrit and "!" or "."
|
||||
local finalText1 = table.concat({
|
||||
"Vous ", 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({
|
||||
"Chance de critique : ", math.Clamp(math.Round(data.calcCrit * 100), 0, 100), "%",
|
||||
" | Pénalités : ", (#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.2, 1.0)
|
||||
|
||||
-- CENTER OF MASS
|
||||
ix.weapons:RegisterHitBox(HITGROUP_CHEST, 40, 0.3, 85, 0.2, 1.0, 0.8)
|
||||
ix.weapons:RegisterHitBox(HITGROUP_STOMACH, 40, 0.4, 85, 0.3, 0.9, 0.7)
|
||||
ix.weapons:RegisterHitBox(HITGROUP_GENERIC, 40, 0.5, 85, 0.4, 0.8, 0.6)
|
||||
ix.weapons:RegisterHitBox(HITGROUP_GEAR, 40, 0.5, 85, 0.4, 0.7, 0.5)
|
||||
|
||||
-- ARM
|
||||
ix.weapons:RegisterHitBox(HITGROUP_LEFTARM, 40, 0.3, 85, 0.2, 0.7, 0.4)
|
||||
ix.weapons:RegisterHitBox(HITGROUP_RIGHTARM, 40, 0.3, 85, 0.2, 0.7, 0.4)
|
||||
|
||||
-- LEG
|
||||
ix.weapons:RegisterHitBox(HITGROUP_LEFTLEG, 40, 0.3, 85, 0.2, 0.7, 0.4)
|
||||
ix.weapons:RegisterHitBox(HITGROUP_RIGHTLEG, 40, 0.3, 85, 0.2, 0.7, 0.4)
|
||||
|
||||
ix.weapons:RegisterWeaponCat("pistol", {
|
||||
name = "Pistolet",
|
||||
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 = "Fusil d'assaut",
|
||||
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 = "Fusil à verrou",
|
||||
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 = "Fusil à pompe",
|
||||
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 = "Mitrailleuse légère",
|
||||
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
|
||||
305
gamemodes/darkrp/plugins/weaponbalance/sv_hooks.lua
Normal file
305
gamemodes/darkrp/plugins/weaponbalance/sv_hooks.lua
Normal file
@@ -0,0 +1,305 @@
|
||||
--[[
|
||||
| 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)
|
||||
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
|
||||
if (victim:Armor() > 0) then
|
||||
if (damage:IsBulletDamage() or damage:IsDamageType(DMG_CLUB) or damage:IsDamageType(DMG_SLASH)) then
|
||||
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
|
||||
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
|
||||
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
|
||||
189
gamemodes/darkrp/plugins/weaponbalance/sv_plugin.lua
Normal file
189
gamemodes/darkrp/plugins/weaponbalance/sv_plugin.lua
Normal 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)
|
||||
152
gamemodes/darkrp/plugins/weaponbalance/sv_util.lua
Normal file
152
gamemodes/darkrp/plugins/weaponbalance/sv_util.lua
Normal 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 a tué %s%s.", arg[1], client:Name(), arg[2] and (" avec " .. arg[2]) or "")
|
||||
end, FLAG_DANGER)
|
||||
|
||||
ix.log.AddType("weaponBalanceResult", function(attacker, result, victim, hitgroup, finalDamage, critChance)
|
||||
return string.format("%s a %s %s%s infligeant %d dégats (Chance de crit : %.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 " dans la "..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
|
||||
Reference in New Issue
Block a user