forked from LandSandBoat/server
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathranged_utilities.lua
More file actions
134 lines (110 loc) · 5.4 KB
/
Copy pathranged_utilities.lua
File metadata and controls
134 lines (110 loc) · 5.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
-----------------------------------
-- Handles the ranged attack and accuracy penalties based on distance from target.
-----------------------------------
xi = xi or {}
xi.combat = xi.combat or {}
xi.combat.ranged = xi.combat.ranged or {}
-----------------------------------
xi.combat.ranged.maxInnerPenalty = 25
xi.combat.ranged.maxOuterPenalty = 20
-- This table provides the default sweet spot ranges for various weapon types, agnostic of mob sizes altogether
xi.combat.ranged.sweetSpotDefaults = {
['throwing'] = { 0.0, 1.3 },
['cannon' ] = { 3.0, 4.3 }, -- needs re-verification
['gun' ] = { 3.0, 4.3 },
['shortbow'] = { 4.0, 6.4 },
['crossbow'] = { 5.0, 8.4 },
['longbow' ] = { 6.0, 9.5 },
}
-- This table provides the sweet spot ranges for weapons, assuming a mob size of 1
-- TODO: this needs re-verification due to better understanding of hitbox sizes
xi.combat.ranged.sweetSpots = {
[xi.item.YOICHINOYUMI_75 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_80 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_85 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_90 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_95 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_99 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_99_II ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_119 ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_119_II ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_119_III ] = { 5.5, 9.5 },
[xi.item.YOICHINOYUMI_119_III_NO_QUIVER] = { 5.5, 9.5 },
}
xi.combat.ranged.getSweetSpotByAttacker = function(attacker)
local weapon = attacker:getEquippedItem(xi.slot.RANGED)
local weaponSkillType = weapon ~= nil and attacker:getWeaponSkillType(xi.slot.RANGED)
local weaponSubSkillType = weapon ~= nil and attacker:getWeaponSubSkillType(xi.slot.RANGED)
local weaponId = weapon and weapon:getID() or 0
local sweetSpotForWeaponId = xi.combat.ranged.sweetSpots[weaponId]
if sweetSpotForWeaponId ~= nil then
return sweetSpotForWeaponId
end
if weaponSkillType == xi.skill.ARCHERY and weaponSubSkillType == 4 then
return xi.combat.ranged.sweetSpotDefaults['longbow']
elseif weaponSkillType == xi.skill.ARCHERY and weaponSubSkillType == 0 then
return xi.combat.ranged.sweetSpotDefaults['shortbow']
elseif weaponSkillType == xi.skill.MARKSMANSHIP and weaponSubSkillType == 0 then
return xi.combat.ranged.sweetSpotDefaults['crossbow']
elseif weaponSkillType == xi.skill.MARKSMANSHIP and weaponSubSkillType == 1 then
return xi.combat.ranged.sweetSpotDefaults['gun']
elseif weaponSkillType == xi.skill.MARKSMANSHIP and weaponSubSkillType == 2 then
return xi.combat.ranged.sweetSpotDefaults['cannon']
end
return xi.combat.ranged.sweetSpotDefaults['throwing']
end
xi.combat.ranged.attackDistancePenalty = function(attacker, defender)
if not attacker:isPC() then
return 0
end
local sweetSpot = xi.combat.ranged.getSweetSpotByAttacker(attacker)
local sweetSpotStart = sweetSpot[1]
local sweetSpotEnd = sweetSpot[2]
local distance = attacker:checkDistance(defender)
local centroidStart = sweetSpotStart + defender:getHitboxSize() + attacker:getHitboxSize()
local centroidEnd = sweetSpotEnd + defender:getHitboxSize() + attacker:getHitboxSize()
local cSkillMax = attacker:getMaxSkillLevel(attacker:getMainLvl(), xi.job.WAR, xi.skill.EVASION)
local penaltyPercentage
if distance < centroidStart then
-- Linear interpolation between 0 and centroidStart of maxInnerPenalty
penaltyPercentage = -xi.combat.ranged.maxInnerPenalty + (xi.combat.ranged.maxInnerPenalty * (distance / centroidStart))
elseif distance <= centroidEnd then
-- No penalty in sweet spot
penaltyPercentage = 0
else
-- Linear interpolation between centroidEnd and 25 of maxOuterPenalty
penaltyPercentage = xi.combat.ranged.maxOuterPenalty * (distance - centroidEnd) / (25 - centroidEnd)
end
local penalty = math.abs(math.ceil((penaltyPercentage / 100) * cSkillMax))
return penalty
end
xi.combat.ranged.accuracyDistancePenalty = function(attacker, defender)
if not attacker:isPC() then
return 0
end
local sweetSpot = xi.combat.ranged.getSweetSpotByAttacker(attacker)
local sweetSpotEnd = sweetSpot[2]
local distance = attacker:checkDistance(defender)
local centroidEnd = sweetSpotEnd + defender:getHitboxSize() + attacker:getHitboxSize()
if distance <= centroidEnd then
return 0
end
-- Linear interpolation between centroidEnd and 25
local penaltyPercentage = (distance - centroidEnd) / (25 - centroidEnd)
local penalty = math.abs(math.floor(penaltyPercentage * (attacker:getMainLvl() / 2)))
return penalty
end
xi.combat.ranged.shouldUseAmmo = function(attacker)
if attacker:isPC() then
local recycleChance = attacker:getMod(xi.mod.RECYCLE) + attacker:getMerit(xi.merit.RECYCLE) + attacker:getJobPointLevel(xi.jp.AMMO_CONSUMPTION)
if attacker:hasStatusEffect(xi.effect.UNLIMITED_SHOT) then
attacker:delStatusEffect(xi.effect.UNLIMITED_SHOT) -- TODO: allegedly Unlimited Shot doesn't remove itself unless you hit
recycleChance = 100
end
if math.randomInt(1, 100) <= recycleChance then
return false
end
return true
end
return false
end