| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- local Player = class()
- Player.radius = 28
- Player.collision = {
- shape = 'circle',
- tag = 'player',
- with = {
- wall = function(self, other, dx, dy)
- if self.z == 0 then
- self.x, self.y = self.x + dx, self.y + dy
- end
- end,
- shortwall = function(self, other, dx, dy)
- if self.z == 0 then
- self.x, self.y = self.x + dx, self.y + dy
- end
- end,
- teamwall = function(self, other, dx, dy)
- if other.team ~= self.team and self.z == 0 then
- self.x, self.y = self.x + dx, self.y + dy
- end
- end,
- player = function(self, other, dx, dy)
- if other.team ~= self.team then
- if self.moving then
- self.x, self.y = self.x + dx, self.y + dy
- end
- end
- end
- }
- }
- ----------------
- -- Core
- ----------------
- function Player:init()
- self.meta = {__index = self}
- self.id = nil
- self.username = ''
- self.class = nil
- self.team = nil
- self.active = false
- self.kills = 0
- self.deaths = 0
- self.x = 0
- self.y = 0
- self.z = 0
- self.angle = 0
- self.drawX = 0
- self.drawY = 0
- self.drawAngle = 0
- self.drawScale = 0
- self.slots = {}
- self.weapon = 1
- self.skill = 1
- self.weaponDirty = false
- self.skillDirty = false
- self.health = 0
- self.maxHealth = 0
- self.shield = 0
- self.lastHurt = -math.huge
- self.lastDamageDealt = -math.huge
- self.ded = false
- self.killer = nil
- self.lifesteal = 0
- self.haste = 0
- self.cloak = 0
- self.stun = 0
- self.disarm = 0
- self.silence = 0
- self.damageOutMultiplier = 1
- self.damageInMultiplier = 1
- self.depth = 0
- self.recoil = 0
- self.alpha = 0
- self.canvasDirty = true
- end
- function Player:activate()
- self.active = true
- self.x = ctx.map.spawn[self.team].x
- self.y = ctx.map.spawn[self.team].y
- self.z = 0
- self.weapon = 1
- self.skill = 1
- table.each(self.slots, function(slot)
- f.exe(slot.deactivate, slot, self)
- end)
- table.clear(self.slots)
- for i = 1, 5 do
- self.slots[i] = setmetatable({}, {__index = self.class.slots[i]})
- f.exe(self.slots[i].activate, self.slots[i], self)
- if self.slots[i].type == 'skill' and self.skill == self.weapon then self.skill = i end
- end
- self.maxHealth = self.class.health
- self.health = self.maxHealth
- self.killer = nil
- ctx.buffs:removeAll(self)
- self.lifesteal = 0
- self.haste = 0
- self.cloak = 0
- self.stun = 0
- self.damageOutMultiplier = 1
- self.damageInMultiplier = 1
- self.depth = -self.id
- self.canvas = nil
- self.canvasDirty = true
- end
- function Player:deactivate()
- self.active = false
- self.username = ''
- end
- function Player:update()
- if self.recoil > 0 then self.recoil = math.lerp(self.recoil, 0, math.min(5 * tickRate, 1)) end
- self.cloak = timer.rot(self.cloak)
- self.stun = timer.rot(self.stun)
- self.disarm = timer.rot(self.disarm)
- self.silence = timer.rot(self.silence)
- if self.ded then
- self.x, self.y = 0, 0
- ctx.event:emit('collision.move', {object = self, resolve = true})
- end
- if ctx.view then
- self.depth = ctx.view:threeDepth(self.x, self.y, self.z)
- if self.z > 0 then self.depth = self.depth - 100 end
- end
- end
- function Player:draw()
- local g, c, x, y, a, s = love.graphics, self.class, self.drawX, self.drawY, self.drawAngle, self.drawScale * self.class.scale
- local alpha = self.alpha * (1 - (self.cloak / ((self.team == ctx.players:get(ctx.id).team or (ctx.players:get(ctx.id).killer and ctx.players:get(ctx.id).killer.id == self.id)) and 2 or 1)))
- if self.canvasDirty then self:refreshCanvas() end
- g.setColor(0, 0, 0, alpha * 50)
- g.draw(c.sprite, self.x + 4, self.y + 4, a, s, s, c.anchorx, c.anchory)
- g.setColor(255, 255, 255, alpha * 255)
- g.draw(self.canvas, x, y, a, s, s, self.canvasAnchorX, self.canvasAnchorY)
- g.setColor(self.team == purple and {222, 200, 230, alpha * 255} or {250, 210, 200, alpha * 255})
- g.draw(c.sprite, x, y, a, s, s, c.anchorx, c.anchory)
- f.exe(self.slots[self.weapon].draw, self.slots[self.weapon], self)
- end
- ----------------
- -- Behavior
- ----------------
- function Player:move(input)
- if self.stun > 0 then return end
- if input.reposition then
- self.x, self.y = input.reposition.x, input.reposition.y
- end
- local w, a, s, d = input.w, input.a, input.s, input.d
- if not (w or a or s or d) then return end
- local up, down, left, right, dx, dy = 1.5 * math.pi, .5 * math.pi, math.pi, 2.0 * math.pi
- if a and not d then dx = left elseif d then dx = right end
- if w and not s then dy = up elseif s then dy = down end
- if not dx then dx = dy end
- if not dy then dy = dx end
- if dx == right and dy == down then dx = 0 end
- local dir = (dx + dy) / 2
- local len = math.max((self.class.speed + self.haste) * tickRate, 0)
- self.x, self.y = self.x + math.dx(len, dir), self.y + math.dy(len, dir)
- self.x = math.clamp(self.x, 0, ctx.map.width)
- self.y = math.clamp(self.y, 0, ctx.map.height)
- self.moving = true
- ctx.event:emit('collision.move', {object = self, resolve = true})
- self.moving = nil
- end
- function Player:turn(input)
- if self.stun > 0 then return end
- local d = math.direction(self.x, self.y, input.x, input.y)
- self.angle = math.anglerp(self.angle, d, math.min(25 * tickRate, 1))
- end
- function Player:slot(input, prev)
- if self.stun > 0 then return end
- input, prev = input or {}, prev or {}
- local ldown, lpress, lrelease = input.l, input.l and not prev.l, not input.l and prev.l
- local rdown, rpress, rrelease = input.r, input.r and not prev.r, not input.r and prev.r
- if input.slot then
- local k = self.slots[input.slot].type
- if self[k] ~= input.slot and (not self.slots[self[k]] or not self.slots[self[k]].targeting) then
- self[k] = input.slot
- local slot = self.slots[input.slot]
- f.exe(slot.select, slot, self)
- self.canvasDirty = true
- end
- end
- local weapon = self.slots[self.weapon]
- local skill = self.slots[self.skill]
- local function fire(slot)
- local obj = self.slots[slot]
- local msg = {id = self.id, slot = slot, mx = obj.needsMouse and input.x, my = obj.needsMouse and input.y}
- obj:fire(self, input.x, input.y)
- if obj.targeted then obj.targeting = false end
- ctx.net:emit(app.net.events.fire, msg)
- end
- for i = 1, 5 do
- if self.slots[i].type ~= 'weapon' or self.weapon == i then
- f.exe(self.slots[i].update, self.slots[i], self)
- end
- end
- if lpress and skill.targeting then
- skill.targeting = false
- self.weaponDirty = true
- end
- if rpress and weapon.targeting then
- weapon.targeting = false
- self.skillDirty = true
- end
- if self.disarm == 0 and weapon:canFire(self) then
- if not ldown then self.weaponDirty = false end
- if not self.weaponDirty then
- if lpress then weapon.targeting = weapon.targeted end
- if (weapon.targeted and weapon.targeting and lrelease) or (not weapon.targeted and ldown) then
- fire(self.weapon)
- end
- end
- end
- if self.silence == 0 and skill:canFire(self) then
- if not rdown then self.skillDirty = false end
- if not self.skillDirty then
- if rpress then skill.targeting = skill.targeted end
- if (skill.targeted and skill.targeting and rrelease) or (not skill.targeted and rdown) then
- fire(self.skill)
- end
- end
- end
- if input.reload then weapon:reload(self) end
- end
- Player.hurt = f.empty
- Player.heal = f.empty
- function Player:die()
- self.ded = 5
- ctx.event:emit('particle.create', {
- kind = 'skull',
- vars = {x = self.x, y = self.y}
- })
- ctx.event:emit('particle.create', {
- kind = 'gib',
- count = 64,
- vars = {x = self.x, y = self.y}
- })
- ctx.event:emit('sound.play', {sound = 'die', x = self.x, y = self.y})
- ctx.buffs:removeAll(self)
- self.alpha = 0
- self.x, self.y = 0, 0
- ctx.event:emit('collision.move', {object = self, resolve = true})
- end
- function Player:spawn()
- self:activate()
- end
- function Player:refreshCanvas()
- local g, c, wep = love.graphics, self.class, self.slots[self.weapon]
- local margin = 16
- local w, h = 0, 0
- self = ctx.players:get(self.id)
- if not wep.image then
- self.canvasAnchorX, self.canvasAnchorY = c.anchorx + margin, c.anchory + margin
- w, h = (2 * margin) + c.sprite:getWidth(), (2 * margin) + c.sprite:getHeight()
- else
- local ws = wep.scale / c.scale
- local ax, ay = c.anchorx, c.anchory
- local px1, py1, px2, py2 = 0, 0, c.sprite:getWidth(), c.sprite:getHeight()
- local dx, dy = c.handx - self.recoil, c.handy
- local wx1 = ax + math.dx(dx, 0) - math.dy(dy, 0) - (wep.anchorx * ws)
- local wy1 = ay + math.dy(dx, 0) + math.dx(dy, 0) - (wep.anchory * ws)
- local wx2 = wx1 + wep.image:getWidth() * ws
- local wy2 = wy1 + wep.image:getHeight() * ws
- if wx1 < 0 then
- ax = ax + wx1
- px1, px2 = px1 + wx1, px2 + wx1
- wx1, wx2 = wx1 + wx1, wx2 + wx1
- end
- if wy1 < 0 then
- ay = ay + wy1
- py1, py2 = py1 + wy1, py2 + wy1
- wy1, wy2 = wy1 + wy1, wy2 + wy2
- end
- px1, py1, px2, py2 = px1 + margin, py1 + margin, px2 + margin, py2 + margin
- wx1, wy1, wx2, wy2 = wx1 + margin, wy1 + margin, wx2 + margin, wy2 + margin
- ax, ay = ax + margin, ay + margin
- w, h = math.max(px2, wx2) + margin, math.max(py2, wy2) + margin
- self.canvasAnchorX, self.canvasAnchorY = ax, ay
- end
- if not self.canvas or self.canvas:getWidth() < w or self.canvas:getHeight() < h then
- self.canvas = g.newCanvas(w, h)
- self.backCanvas = g.newCanvas(w, h)
- end
- g.pop()
- if self.team == purple then
- self.canvas:clear(190, 160, 220, 0)
- self.backCanvas:clear(190, 160, 220, 0)
- g.setColor(190, 160, 220)
- else
- self.canvas:clear(240, 160, 140, 0)
- self.backCanvas:clear(240, 160, 140, 0)
- g.setColor(240, 160, 140)
- end
- self.canvas:renderTo(function()
- g.setShader(data.media.shaders.colorize)
- g.draw(c.sprite, self.canvasAnchorX, self.canvasAnchorY, 0, 1, 1, c.anchorx, c.anchory)
- if wep.image then
- local dx, dy = c.handx - self.recoil, c.handy
- local wx = self.canvasAnchorX + math.dx(dx, 0) - math.dy(dy, 0)
- local wy = self.canvasAnchorY + math.dy(dx, 0) + math.dx(dy, 0)
- g.draw(wep.image, wx, wy, 0, wep.scale / c.scale, wep.scale / c.scale, wep.anchorx, wep.anchory)
- end
- g.setShader()
- end)
- data.media.shaders.horizontalBlur:send('amount', .008)
- data.media.shaders.verticalBlur:send('amount', .008)
- g.setColor(255, 255, 255)
- for i = 1, 3 do
- g.setShader(data.media.shaders.horizontalBlur)
- self.backCanvas:renderTo(function()
- g.draw(self.canvas)
- end)
- g.setShader(data.media.shaders.verticalBlur)
- self.canvas:renderTo(function()
- g.draw(self.backCanvas)
- end)
- end
- g.setShader()
- ctx.view:worldPush()
- self.canvasDirty = false
- end
- return Player
|