This repository has been archived by the owner on May 20, 2023. It is now read-only.
/
boss_reward_chest.lua
243 lines (210 loc) · 7.05 KB
/
boss_reward_chest.lua
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
local function pushSeparated(buffer, sep, ...)
local argv = {...}
local argc = #argv
for k, v in ipairs(argv) do
table.insert(buffer, v)
if k < argc and sep then
table.insert(buffer, sep)
end
end
end
local function insertItems(buffer, info, parent, items)
local start = info.running
for _, item in ipairs(items) do
if item ~= nil then
if _ ~= 1 or parent > 100 then
table.insert(buffer, ",")
end
info.running = info.running + 1
table.insert(buffer, "(")
pushSeparated(buffer, ",", info.playerGuid, parent, info.running, item:getId(), item:getSubType(), db.escapeString(serializeAttributes(item)))
table.insert(buffer, ")")
if item:isContainer() then
local size = item:getSize()
if size > 0 then
local subItems = {}
for i = 1, size do
table.insert(subItems, item:getItem(i - 1))
end
insertItems(buffer, info, info.running, subItems)
end
end
end
end
return info.running - start
end
local function insertRewardItems(playerGuid, timestamp, itemList)
db.asyncStoreQuery('select `pid`, `sid` from `player_rewards` where player_id = ' .. playerGuid .. ' order by `sid` ASC;',
function(query)
local lastReward = 0
local lastStoreId
if(query) then
repeat
local sid = result.getDataInt(query, 'sid')
local pid = result.getDataInt(query, 'pid')
if pid < 100 then
lastReward = pid
end
lastStoreId = sid
until not result.next(query)
end
local buffer = {'INSERT INTO `player_rewards` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES'}
--reward bag
local info = {
playerGuid = playerGuid,
running = lastStoreId or 100
}
local bag = Game.createItem(ITEM_REWARD_CONTAINER)
bag:setAttribute(ITEM_ATTRIBUTE_DATE, timestamp)
if itemList then
for _, p in ipairs(itemList) do
bag:addItem(p[1], p[2])
end
end
local total = insertItems(buffer, info, lastReward + 1, {bag})
table.insert(buffer, ";")
if total ~= 0 then
db.query(table.concat(buffer))
end
end
)
end
local function getPlayerStats(bossId, playerGuid, autocreate)
local ret = globalBosses[bossId][playerGuid]
if not ret and autocreate then
ret = {
bossId = bossId,
damageIn = 0, -- damage taken from the boss
healing = 0, -- healing (other players) done
}
globalBosses[bossId][playerGuid] = ret
return ret
end
return ret
end
function onDeath(creature, corpse, killer, mostDamageKiller, lastHitUnjustified, mostDamageUnjustified)
-- player
if creature:isPlayer() then
for bossId, tb in pairs(globalBosses) do
for playerId, tb2 in pairs(tb) do
if playerId == Player(creature:getId()):getGuid() then
globalBosses[bossId][playerId] = nil
end
end
end
return
end
-- boss
local monsterType = creature:getType()
if monsterType:isRewardBoss() then -- Make sure it is a boss
local bossId = creature:getId()
local timestamp = os.time()
local totalDamageOut, totalDamageIn, totalHealing = 0.1, 0.1, 0.1 -- avoid dividing by zero
local scores = {}
local info = globalBosses[bossId]
local damageMap = creature:getDamageMap()
for guid, stats in pairs(info) do
local player = Player(stats.playerId)
local part = damageMap[stats.playerId]
local damageOut, damageIn, healing = (stats.damageOut or 0) + (part and part.total or 0), stats.damageIn or 0, stats.healing or 0
totalDamageOut = totalDamageOut + damageOut
totalDamageIn = totalDamageIn + damageIn
totalHealing = totalHealing + healing
table.insert(scores, {
player = player,
guid = guid,
damageOut = damageOut,
damageIn = damageIn,
healing = healing,
})
end
local participants = 0
for _, con in ipairs(scores) do
local score = (con.damageOut / totalDamageOut) + (con.damageIn / totalDamageIn) + (con.healing / totalHealing)
con.score = score / 3 -- normalize to 0-1
if score ~= 0 then
participants = participants + 1
end
end
table.sort(scores, function(a, b) return a.score > b.score end)
local expectedScore = 1 / participants
for _, con in ipairs(scores) do
local reward, stamina -- ignoring stamina for now because I heard you get receive rewards even when it's depleted
if con.player then
reward = con.player:getReward(timestamp, true)
stamina = con.player:getStamina()
else
stamina = con.stamina or 0
end
local playerLoot
if --[[stamina > 840 and]] con.score ~= 0 then
local lootFactor = 1
lootFactor = lootFactor / participants ^ (1 / 3) -- tone down the loot a notch if there are many participants
lootFactor = lootFactor * (1 + lootFactor) ^ (con.score / expectedScore) -- increase the loot multiplicatively by how many times the player surpassed the expected score
playerLoot = monsterType:getBossReward(lootFactor, _ == 1)
if con.player then
for _, p in ipairs(playerLoot) do
reward:addItem(p[1], p[2])
end
end
end
if con.player and con.score ~= 0 then
local lootMessage = ("The following items dropped by %s are available in your reward chest: %s"):format(creature:getName(), reward:getContentDescription())
if stamina > 840 then
reward:getContentDescription(lootMessage)
end
con.player:sendTextMessage(MESSAGE_LOOT, lootMessage)
elseif con.score ~= 0 then
insertRewardItems(con.guid, timestamp, playerLoot)
end
end
globalBosses[bossId] = nil
end
return true
end
function onThink(creature, interval)
local bossId = creature:getId()
local info = globalBosses[bossId]
-- Reset all players' status
for _, player in pairs(info) do
player.active = false
end
-- Set all players in boss' target list as active in the fight
local targets = creature:getTargetList()
for _, target in ipairs(targets) do
if target:isPlayer() then
local stats = getPlayerStats(bossId, target:getGuid(), true)
stats.playerId = target:getId() -- Update player id
stats.active = true
end
end
end
function onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
if not next(globalBosses) then
return primaryDamage, primaryType, secondaryDamage, secondaryType
end
if not creature or not attacker then
return primaryDamage, primaryType, secondaryDamage, secondaryType
end
local stats = creature:inBossFight()
if not stats then
return primaryDamage, primaryType, secondaryDamage, secondaryType
end
local creatureId, attackerId = creature:getId(), attacker:getId()
stats.playerId = creatureId -- Update player id
-- Account for healing of others active in the boss fight
if primaryType == COMBAT_HEALING and attacker:isPlayer() and attackerId ~= creatureId then
local healerStats = getPlayerStats(stats.bossId, attacker:getGuid(), true)
healerStats.active = true
healerStats.playerId = attackerId -- Update player id
healerStats.healing = healerStats.healing + primaryDamage
elseif stats.bossId == attackerId then
-- Account for damage taken from the boss
stats.damageIn = stats.damageIn + primaryDamage
end
return primaryDamage, primaryType, secondaryDamage, secondaryType
end
function onLogin(player)
player:registerEvent("BossDeath")
return true
end