| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- local PlayerServer = extend(app.player.base)
- function PlayerServer:activate()
- self.shields = {}
- self.hurtHistory = {}
- self.helpHistory = {}
- self.history = {}
- self.ack = tick
- self.spawnTimer = 0
- self.spawnMultiplier = 0
- app.player.base.activate(self)
- end
- function PlayerServer:get(t)
- if not self.history or t == tick then return self end
- while self.history[1] and self.history[1].tick < tick - 2 / tickRate do
- table.remove(self.history, 1)
- end
- if #self.history == 0 then return self end
- if self.history[#self.history].tick < t then return self end
- for i = #self.history, 1, -1 do
- if self.history[i].tick <= t then return self.history[i] end
- end
- return self.history[1]
- end
- function PlayerServer:update()
- if self.ded then
- self:time()
- return app.player.base.update(self)
- end
- self:time()
- self:logic()
- if self.health < self.maxHealth and not self.ded then
- local percentage = ((tick - self.lastHurt) - (3 / tickRate)) / (10 / tickRate)
- if percentage > 0 then
- percentage = (1 + (percentage * 7)) / 100
- self:heal({amount = self.maxHealth * percentage * tickRate})
- end
- end
- self.spawnTimer = timer.rot(self.spawnTimer)
- if self.spawnTimer == 0 then
- local spawn = ctx.collision:circleTest(self.x, self.y, self.radius, {tag = 'spawnroom'})
- if spawn then
- self.spawnMultiplier = self.spawnMultiplier + 1
- local amount = 8 * self.spawnMultiplier
- if spawn.team == self.team then
- self.health = math.min(self.health + amount, self.maxHealth)
- else
- ctx.event:emit(app.net.events.damage, {id = self.id, amount = amount, from = self.id, tick = tick})
- end
- else
- self.spawnMultiplier = 0
- end
- self.spawnTimer = .5
- end
- app.player.base.update(self)
- end
- function PlayerServer:trace(data, ping)
- if data.tick > self.ack then
- -- Lag compensation
- local oldData = {}
- local newData = {}
- if ping > 0 then
- local target = data.tick - (((ping / 2) / 1000) + interp) / tickRate
- local t1 = math.floor(target)
- local factor = target - t1
- ctx.players:each(function(p)
- if p.id ~= self.id then
- oldData[p.id] = {p.x, p.y, p.angle}
- local s1, s2 = p:get(t1), p:get(t1 + 1)
- s1 = {x = s1.x, y = s1.y, angle = s1.angle}
- s2 = {x = s2.x, y = s2.y, angle = s2.angle}
- local lerpd = table.interpolate(s1, s2, factor)
- p.x = lerpd.x
- p.y = lerpd.y
- newData[p.id] = {p.x, p.y}
- p.angle = lerpd.angle
- ctx.event:emit('collision.move', {object = p, resolve = true})
- end
- end)
- end
- self.ack = data.tick
- local prev = self:get(data.tick - 1)
- if prev then prev = prev.input end
- if not self.ded then
- self:move(data)
- self:turn(data)
- self:slot(data, prev)
- end
- table.insert(self.history, setmetatable({
- x = self.x,
- y = self.y,
- z = self.z,
- angle = self.angle,
- input = data,
- tick = data.tick
- }, self.meta))
- -- sync
- local msg = {}
- msg.x = math.round(self.x * 10)
- msg.y = math.round(self.y * 10)
- msg.z = math.round(self.z)
- msg.angle = math.round((math.deg(self.angle) + 360) % 360)
- local shield = 0
- table.each(self.shields, function(s) shield = shield + s.health end)
- msg.health = math.round(self.health)
- msg.shield = math.round(shield)
- if data.slot then
- msg.weapon = self.weapon
- msg.skill = self.skill
- end
- msg.id = self.id
- msg.tick = tick
- msg.ack = self.ack
- ctx.net:emit(app.net.events.sync, msg)
- -- Undo lag compensation
- ctx.players:each(function(p)
- if oldData[p.id] then
- local offsetx, offsety = p.x - newData[p.id][1], p.y - newData[p.id][2]
- p.x, p.y, p.angle = unpack(oldData[p.id])
- p.x, p.y = p.x + offsetx, p.y + offsety
- end
- end)
- end
- end
- function PlayerServer:time()
- self.ded = timer.rot(self.ded, function() ctx.net:emit(app.net.events.spawn, {id = self.id}) end)
- if self.ded == 0 then self.ded = false end
- end
- function PlayerServer:spell(kind)
- app.player.base.spell(self, kind)
- end
- function PlayerServer:hurt(data)
- if self.ded then return end
- local target = self.shields[1] or self
- while #self.hurtHistory > 0 and self.hurtHistory[1].tick < tick - (10 / tickRate) do
- table.remove(self.hurtHistory, 1)
- end
- table.insert(self.hurtHistory, {tick = data.tick, amount = data.amount, from = data.from})
- target.health = math.max(target.health - data.amount, 0)
- if target.health <= 0 then
- if target == self then
- self.ded = 5
- local playerHurt = {}
- for i = 1, 16 do playerHurt[i] = {i, 0} end
- playerHurt[data.from][2] = -1
- playerHurt[self.id][2] = -1
- for i, v in ipairs(self.hurtHistory) do
- if v.from ~= data.from and v.from ~= self.id then
- playerHurt[v.from][2] = playerHurt[v.from][2] + v.amount * (1 - ((data.tick - v.tick) / (10 / tickRate)))
- end
- end
- table.sort(playerHurt, function(a, b)
- return a[2] > b[2]
- end)
- local assists = {}
- if playerHurt[1][2] > 0 then
- table.insert(assists, {id = playerHurt[1][1]})
- if playerHurt[2][2] > 0 then table.insert(assists, {id = playerHurt[2][1]}) end
- end
- ctx.net:emit(app.net.events.dead, {id = self.id, kill = data.from, assists = assists})
- else
- f.exe(self.shields[1].callback, self)
- table.remove(self.shields, 1)
- end
- end
- end
- function PlayerServer:heal(data)
- self.health = math.min(self.health + data.amount, self.maxHealth)
- end
- function PlayerServer:spawn()
- table.clear(self.hurtHistory)
- table.clear(self.helpHistory)
- app.player.base.spawn(self)
- end
- PlayerServer.logic = f.empty
- return PlayerServer
|