Created
January 10, 2020 16:26
-
-
Save a327ex/1b2ac2a19cac37617cd9f605a115aec0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function init() | |
white = {1, 1, 1, 1} | |
black = {0, 0, 0, 1} | |
red = {1.0, 0.1, 0.2, 1} | |
doodle = g.newShader("doodle.frag") | |
doodle:send("doodle_max_offset", {0.0020, 0.0020}) | |
doodle:send("doodle_noise_scale", {6, 8}) | |
doodle:send("doodle_frame_count", 8) | |
doodle:send("doodle_frame_time", 0.2) | |
combine = g.newShader("combine.frag") | |
doodle_canvas = g.newCanvas(gw, gh) | |
game_canvas = g.newCanvas(gw, gh) | |
new_animation("hit1", 96, 47) | |
new_animation("smoke1", 50, 50, {1}) | |
player = Player(gw/2, gh - 6) | |
projectiles = {} | |
effects = {} | |
enemies = {} | |
ui = {} | |
ammo_bars = {} | |
hp_bars = {} | |
timer:every(2, function() | |
table.insert(enemies, Enemy1(rng:float(60, gw - 60), -60)) | |
end) | |
local h = (gh - 55) | |
local bh = h/player.max_ammo | |
for i = 1, player.max_ammo do table.insert(ammo_bars, UIBar(gw - 24, 50 + (i-1)*(bh), bh - 3, "ammo")) end | |
player_ammo_ui = PlayerAmmoUI(gw - 24, 22) | |
local hh = (gh/2)/player.max_hp | |
for i = 1, player.max_hp do table.insert(hp_bars, UIBar(24, 55 + (i-1)*(hh), hh - 3, "hp")) end | |
player_hp_ui = PlayerHPUI(24, 22) | |
end | |
function update(dt) | |
doodle:send("time", time) | |
player:update(dt) | |
update_objects(projectiles, dt) | |
update_objects(enemies, dt) | |
update_objects(effects, dt) | |
update_objects(ammo_bars, dt) | |
update_objects(hp_bars, dt) | |
player_ammo_ui:update(dt) | |
player_hp_ui:update(dt) | |
end | |
function draw() | |
g.setCanvas(game_canvas) | |
g.clear() | |
g.setColor(white) | |
camera:attach() | |
draw_objects(projectiles) | |
draw_objects(enemies) | |
draw_objects(effects) | |
draw_objects(ammo_bars) | |
draw_objects(hp_bars) | |
player_ammo_ui:draw() | |
player_hp_ui:draw() | |
player:draw() | |
camera:detach() | |
g.setCanvas() | |
g.setCanvas(doodle_canvas) | |
g.clear() | |
g.setColor(white) | |
g.setShader(doodle) | |
g.draw(game_canvas, 0, 0, 0, 1, 1) | |
g.setShader() | |
g.setCanvas() | |
g.setColor(1, 1, 1, 1) | |
g.setBlendMode("alpha", "premultiplied") | |
g.draw(doodle_canvas, 0, 0, 0, sx, sy) | |
g.setBlendMode("alpha") | |
end | |
Player = Class:extend() | |
function Player:new(x, y) | |
self.x, self.y = x, y | |
self.rs = 16 | |
self.sx, self.sy = 1, 1 | |
self.e1x, self.e1y = -self.rs/2.5, -self.rs/2.5 | |
self.e2x, self.e2y = self.rs/2.5, -self.rs/2.5 | |
self.e1ox, self.e1oy = 0, 0 | |
self.e2ox, self.e2oy = 0, 0 | |
self.r = 0 | |
self.scale_spring = Spring(1) | |
timer:everyi(2, function() timer:tween(1, self, {sx = 1.025, sy = 1.025}, cubic_in, function() timer:tween(1, self, {sx = 1, sy = 1}, linear) end) end) | |
self.rso = 0; timer:everyi({0.1, 0.2}, function() self.rso = rng:float(-1, 1) end) | |
self.attack_spring = Spring(1) | |
self.attack_timer = 0 | |
self.attack_pulse_timer = 0 | |
self.attack_cd = 0.06 | |
self.shape = HC.circle(self.x, self.y, 16) | |
self.shape.parent = self | |
self.max_ammo = 35 | |
self.ammo = self.max_ammo | |
self.max_hp = 10 | |
self.hp = self.max_hp | |
self.reloading = false | |
self.reload_timer = 0 | |
self.reload_cd = 0.6 | |
self.reload_spring = Spring(1) | |
end | |
function Player:update(dt) | |
self.scale_spring:update(dt) | |
self.attack_spring:update(dt) | |
self.reload_spring:update(dt) | |
self.s = self.scale_spring.x*self.attack_spring.x*self.reload_spring.x | |
self.r = angle_to_mouse(self.x, self.y) | |
if m.isDown(1) and not m.isDown(2) then | |
self.attack_timer = self.attack_timer + dt | |
if self.attack_timer > self.attack_cd and self.ammo > 0 then | |
self.attack_timer = 0 | |
local r = rng:float(-6, 6) | |
local v = rng:float(600, 700) | |
table.insert(projectiles, Projectile(self.x + self.s*1.5*self.rs*math.cos(self.r) + r*math.cos(self.r + math.pi/2), self.y + self.s*1.5*self.rs*math.sin(self.r) + r*math.sin(self.r + math.pi/2), v*math.cos(self.r), v*math.sin(self.r))) | |
self.scale_spring:pull(0.1) | |
for i = 1, 4 do table.insert(effects, EllipseParticle(self.x + self.s*1.5*self.rs*math.cos(self.r), self.y + self.s*1.5*self.rs*math.sin(self.r), self.r + rng:float(-math.pi/2, math.pi/2), rng:float(100, 400))) end | |
table.insert(effects, ShootCircle(self.x + self.s*1.5*self.rs*math.cos(self.r), self.y + self.s*1.5*self.rs*math.sin(self.r))) | |
table.insert(effects, ShootCapsule(self.x + self.s*1.5*self.rs*math.cos(self.r), self.y + self.s*1.5*self.rs*math.sin(self.r), self.r + rng:float(-math.pi/4, math.pi/4), rng:float(100, 300))) | |
player_ammo_ui:jiggle() | |
ammo_bars[self.ammo]:spend() | |
self.ammo = self.ammo - 1 | |
end | |
self.attack_pulse_timer = self.attack_pulse_timer + dt | |
if self.attack_pulse_timer > 4*self.attack_cd then | |
self.attack_pulse_timer = 0 | |
self.attack_spring:pull(0.25) | |
end | |
end | |
local r = math.deg(self.r) | |
local mx = camera:get_mouse_position() | |
local rd = 1 | |
if mx > self.x then rd = -1 else rd = 1 end | |
if mouse_pressed(2) then | |
self.reloading = true | |
self.reload_spring:pull(0.4) | |
self.reload_text = ReloadText(self.x + rd*62, self.y - 14, self.reload_cd, rd*math.pi/32) | |
table.insert(effects, self.reload_text) | |
end | |
if mouse_released(2) then | |
self.reloading = false | |
self.reload_spring:pull(0.2) | |
if self.reload_text then | |
self.reload_text:die() | |
self.reload_text = nil | |
end | |
end | |
if self.reloading then | |
self.reload_timer = self.reload_timer + dt | |
if self.reload_text then self.reload_text.t = self.reload_timer/self.reload_cd end | |
player_ammo_ui.t = self.reload_timer/self.reload_cd | |
if self.reload_timer > self.reload_cd then | |
for i = 1, self.max_ammo do ammo_bars[i]:refresh() end | |
player_ammo_ui:refresh() | |
table.insert(effects, CircleEffect(self.x, self.y, self.s*self.rs)) | |
self.reload_spring:pull(0.4) | |
self.reload_timer = 0 | |
self.ammo = self.max_ammo | |
if self.reload_text then | |
self.reload_text:die() | |
self.reload_text = nil | |
end | |
end | |
else self.reload_timer = 0 end | |
self.shape:moveTo(self.x, self.y) | |
end | |
function Player:draw() | |
push(self.x, self.y, 0, self.s*self.sx, self.s*self.sy) | |
circle(self.x, self.y, self.rs + self.rso, 2) | |
pop() | |
circlef(self.x + self.s*1.5*self.rs*math.cos(self.r), self.y + self.s*1.5*self.rs*math.sin(self.r), 3) | |
end | |
function Player:hit(damage) | |
self.hp = self.hp - 1 | |
slow(0.5, 0.5) | |
flash(2, black) | |
end | |
PlayerHPUI = Class:extend() | |
function PlayerHPUI:new(x, y) | |
self.timer = Timer() | |
self.x, self.y = x, y | |
self.sx, self.sy = 1.25, 1.25 | |
self.scale_spring = Spring(1) | |
end | |
function PlayerHPUI:update(dt) | |
self.timer:update(dt) | |
self.scale_spring:update(dt) | |
self.hp = player.hp | |
self.max_hp = player.max_hp | |
end | |
function PlayerHPUI:draw() | |
push(self.x, self.y, 0, self.scale_spring.x*self.sx, self.scale_spring.x*self.sy) | |
draw_text(self.hp, self.x, self.y, 0, 1, 1, font_medium) | |
pop() | |
end | |
function PlayerHPUI:refresh() | |
self.scale_spring:pull(0.5) | |
end | |
function PlayerHPUI:jiggle() | |
self.scale_spring:pull(0.2) | |
end | |
PlayerAmmoUI = Class:extend() | |
function PlayerAmmoUI:new(x, y) | |
self.timer = Timer() | |
self.x, self.y = x, y | |
self.sx, self.sy = 1.25, 1.25 | |
self.oy = 0 | |
self.scale_spring = Spring(1) | |
self.timer:tween(0.1, self, {sx = 1, sy = 1}, cubic_in, function() self.sx, self.sy = 1, 1 end) | |
end | |
function PlayerAmmoUI:update(dt) | |
self.timer:update(dt) | |
self.scale_spring:update(dt) | |
self.ammo = player.ammo | |
self.max_ammo = player.max_ammo | |
end | |
function PlayerAmmoUI:draw() | |
if player.reloading then | |
if player.reload_text then | |
if not player.reload_text.visible then return end | |
end | |
end | |
push(self.x, self.y + self.oy, 0, self.scale_spring.x*self.sx, self.scale_spring.x*self.sy) | |
draw_text(self.ammo, self.x, self.y + self.oy, 0, 1, 1, font_medium) | |
pop() | |
if player.reloading then | |
local x = self.x | |
local y1 | |
for i = 1, #ammo_bars do | |
if ammo_bars[i].spent then | |
y1 = ammo_bars[i].y | |
break | |
end | |
end | |
local y2 = ammo_bars[#ammo_bars].y | |
if y1 then | |
g.setLineWidth(4) | |
g.line(x, y1, x, y1 + (y2-y1)*self.t) | |
g.setLineWidth(1) | |
end | |
end | |
end | |
function PlayerAmmoUI:refresh() | |
self.t = 0 | |
self.scale_spring:pull(0.5) | |
end | |
function PlayerAmmoUI:jiggle() | |
self.scale_spring:pull(0.2) | |
end | |
UIBar = Class:extend() | |
function UIBar:new(x, y, h, type) | |
self.type = type | |
self.timer = Timer() | |
self.x, self.y = x, y | |
self.sx, self.sy = 1.25, 1.25 | |
self.w = 16 | |
self.ow = 16 | |
self.h = h | |
self.spent = false | |
self.scale_spring = Spring(1) | |
self.timer:tween(0.1, self, {sx = 1, sy = 1}, cubic_in, function() self.sx, self.sy = 1, 1 end, "refreshs") | |
end | |
function UIBar:update(dt) | |
self.timer:update(dt) | |
self.scale_spring:update(dt) | |
end | |
function UIBar:draw() | |
if self.type == "ammo" then | |
if player.reloading then | |
if player.reload_text then | |
if not player.reload_text.visible then return end | |
end | |
end | |
end | |
push(self.x, self.y, 0, self.sx*self.scale_spring.x, self.sy*self.scale_spring.x) | |
if self.fake_spent then rect(self.x, self.y, self.w, self.h, nil, nil, 2, white) | |
else rectf(self.x, self.y, self.w, self.h, nil, nil, white) end | |
pop() | |
end | |
function UIBar:refresh() | |
self.spent = false | |
self.fake_spent = false | |
self.scale_spring:pull(0.4) | |
self.timer:tween(0.05, self, {w = self.ow}, linear, function() self.w = self.ow end, "refresh") | |
end | |
function UIBar:spend() | |
self.spent = true | |
self.scale_spring:pull(0.4) | |
self.timer:tween(0.05, self, {w = 0}, linear, function() self.w = 0 end, "spend") | |
table.insert(effects, ShootCircle(self.x, self.y)) | |
table.insert(effects, ShootCapsule(self.x, self.y, rng:float(-math.pi/4, 0), rng:float(50, 150))) | |
end | |
function UIBar:fake_spend() | |
self.fake_spent = true | |
self.scale_spring:pull(0.4) | |
end | |
function UIBar:spend2() | |
self.spent = true | |
self.scale_spring:pull(0.4) | |
table.insert(effects, ShootCircle(self.x, self.y, 24)) | |
self.timer:tween(0.1, self, {w = 0}, linear, function() self.w = 0 end, "spend") | |
for i = 1, 8 do table.insert(effects, DeathParticle(self.x, self.y, rng:float(0, 2*math.pi), rng:float(100, 400))) end | |
end | |
CircleEffect = Class:extend() | |
function CircleEffect:new(x, y, r, color) | |
self.x, self.y = x, y | |
self.r = r | |
self.lw = 8 | |
self.color = color or white | |
timer:tween(0.15, self, {r = 4*self.r, lw = 1}, linear, function() self.dead = true end) | |
end | |
function CircleEffect:update(dt) | |
end | |
function CircleEffect:draw() | |
circle(self.x, self.y, self.r, self.lw, self.color) | |
end | |
ReloadText = Class:extend() | |
function ReloadText:new(x, y, duration, r) | |
self.timer = Timer() | |
self.x, self.y = x, y | |
self.r = r | |
self.sx, self.sy = 1, 1 | |
self.characters = {} | |
self.visuals = {1, 1, 1, 1, 1, 1} | |
self.visible = true | |
self.font = font_medium | |
self.w, self.h = self.font:getWidth("RELOAD"), self.font:getHeight() | |
self.scale_spring = Spring(1) | |
self.scale_spring:pull(0.15) | |
self.t = 0 | |
local characters = {"R", "E", "L", "O", "A", "D"} | |
for i = 1, 6 do | |
self.timer:after((i-1)*(0.15/6), function() | |
self.visuals[i] = 3 | |
if self.visuals[i-1] then self.visuals[i-1] = 2 end | |
if self.visuals[i-2] then self.visuals[i-2] = 1 end | |
table.insert(self.characters, characters[i]) | |
end) | |
end | |
self.timer:after(0.15, function() | |
self.visuals = {1, 1, 1, 1, 1, 1} | |
self.timer:every(0.05, function() self.visible = not self.visible end, math.floor((duration - 0.15)/0.05)) | |
self.timer:after((duration - 0.15), function() self.visible = true end) | |
self.timer:every(0.035, function() | |
local random_characters = "0123456789abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWYXZ" | |
for i, c in ipairs(self.characters) do | |
if rng:bool(10) then | |
local r = rng:int(1, #random_characters) | |
self.characters[i] = random_characters:sub(r, r) | |
end | |
if rng:bool(10) then self.visuals[i] = rng:int(1, 4) end | |
end | |
end, math.floor((duration - 0.15)/0.035)) | |
end) | |
end | |
function ReloadText:update(dt) | |
self.timer:update(dt) | |
self.scale_spring:update(dt) | |
end | |
function ReloadText:draw() | |
if not self.visible then return end | |
local w, h = 0, 0 | |
local x, y = self.x - self.w/2, self.y - self.h/2 | |
g.setFont(self.font) | |
push(self.x, self.y, self.r, self.scale_spring.x*self.sx, self.scale_spring.x*self.sy) | |
for i = 1, #self.characters do | |
local cw, ch = self.font:getWidth(self.characters[i]), self.font:getHeight() | |
if self.visuals[i] == 1 then | |
g.setColor(white) | |
g.print(self.characters[i], x + w, y + h) | |
elseif self.visuals[i] == 2 then | |
g.setColor(white) | |
rectf(x + w + cw/2, y + h + ch/2, cw, ch) | |
g.setColor(black) | |
g.print(self.characters[i], x + w, y + h) | |
elseif self.visuals[i] == 3 then | |
g.setColor(white) | |
rectf(x + w + cw/2, y + h + ch/2, cw, ch) | |
elseif self.visuals[i] == 4 then | |
g.setColor(black) | |
rectf(x + w + cw/2, y + h + ch/2, cw, ch) | |
end | |
w = w + self.font:getWidth(self.characters[i]) | |
end | |
g.setColor(white) | |
g.rectangle("fill", x, y - 5, self.w*self.t, 3) | |
pop() | |
end | |
function ReloadText:die() | |
self.dead = true | |
end | |
RefreshEffect = Class:extend() | |
function RefreshEffect:new(x, y, w, h) | |
self.x, self.y = x, y | |
self.w, self.h = w, h | |
self.oy = h/3 | |
timer:tween(0.15, self, {h = 0}, linear, function() self.dead = true end) | |
end | |
function RefreshEffect:update(dt) | |
end | |
function RefreshEffect:draw() | |
g.rectangle("fill", self.x - self.w/2, self.y - self.oy, self.w, self.h) | |
end | |
Projectile = Class:extend() | |
function Projectile:new(x, y, vx, vy, opts) | |
for k, v in pairs(opts or {}) do self[k] = v end | |
self.x, self.y = x, y | |
self.vx, self.vy = vx, vy | |
self.sx, self.sy = 1, 1 | |
self.w, self.h = 16, 4 | |
self.shape = HC.rectangle(self.x - self.w/2, self.y - self.h/2, self.w, self.h) | |
self.shape.parent = self | |
self.damage = rng:int(40, 60) | |
end | |
function Projectile:update(dt) | |
self.vy = self.vy + 300*dt | |
self.x = self.x + self.vx*dt | |
self.y = self.y + self.vy*dt | |
self.r = math.atan2(self.vy, self.vx) | |
if self.x < 0 then self:die(0) end | |
if self.x > gw then self:die(math.pi) end | |
if self.y < 0 then self:die(math.pi/2) end | |
if self.y > gh then self:die(-math.pi/2) end | |
self.shape:moveTo(self.x, self.y) | |
self.shape:setRotation(self.r) | |
end | |
function Projectile:draw() | |
push(self.x, self.y, self.r, self.sx, self.sy) | |
rectf(self.x, self.y, self.w, self.h, nil, nil, white) | |
pop() | |
end | |
function Projectile:die(r) | |
self.dead = true | |
for i = 1, 2 do table.insert(effects, EllipseParticle(self.x, self.y, (r or math.atan2(-self.vy, -self.vx)) + rng:float(-math.pi/4, math.pi/4), rng:float(100, 300))) end | |
table.insert(effects, ShootCircle(self.x, self.y)) | |
end | |
Enemy1 = Class:extend() | |
function Enemy1:new(x, y) | |
self.timer = Timer() | |
self.x, self.y = x, y | |
self.w = 16 | |
self.r = 0 | |
self.sx, self.sy = 1, 1 | |
self.d = 1 | |
self.v = 100 | |
self.vs = {} | |
local r = 0 | |
for i = 1, 8 do | |
local w = rng:float(0.95, 1.05) | |
self.vs[2*(i-1)+1] = self.w*w*math.cos(r) | |
self.vs[2*i] = self.w*w*math.sin(r) | |
r = r + math.pi/4 | |
end | |
self.vr = rng:float(-4*math.pi, 4*math.pi) | |
self.shape = HC.polygon(unpack(to_polygon(self.x, self.y, self.vs))) | |
self.shape.parent = self | |
self.hp = 800 | |
end | |
function Enemy1:update(dt) | |
self.timer:update(dt) | |
self.y = self.y + self.d*self.v*dt | |
self.r = self.r + self.vr*dt | |
self.shape:moveTo(self.x, self.y) | |
self.shape:setRotation(self.r) | |
for other in pairs(HC.neighbors(self.shape)) do | |
if other.parent:is(Projectile) then | |
local collides = self.shape:collidesWith(other) | |
if collides then | |
self:hit(other.parent.damage, other.parent.r) | |
other.parent:die() | |
table.insert(effects, HitEffect(other.parent.x, other.parent.y)) | |
end | |
end | |
end | |
if self.y > gh then | |
self.fast = true | |
self.d = -1 | |
self.v = 150 | |
self.y = gh - 2 | |
table.insert(effects, DeathCircle(self.x, self.y, 2*self.w, red)) | |
table.insert(effects, CircleEffect(self.x, self.y, self.w, red)) | |
for i = player.max_hp, 1, -1 do | |
if not hp_bars[i].spent and not hp_bars[i].fake_spent then | |
hp_bars[i]:fake_spend() | |
break | |
end | |
end | |
flash(1, black) | |
end | |
if self.y < 0 and self.fast then | |
self.dead = true | |
player:hit(1) | |
table.insert(effects, DeathCircle(self.x, self.y, 2*self.w, red)) | |
table.insert(effects, CircleEffect(self.x, self.y, self.w, red)) | |
for i = player.max_hp, 1, -1 do | |
if hp_bars[i].fake_spent and not hp_bars[i].spent then | |
hp_bars[i]:spend2() | |
break | |
end | |
end | |
end | |
end | |
function Enemy1:draw() | |
if self.y > gh + 64 then return end | |
local color = red | |
if self.hit_flash then color = white end | |
push(self.x, self.y, self.r, self.sx, self.sy) | |
polygon(to_polygon(self.x, self.y, self.vs), 2, color) | |
pop() | |
end | |
function Enemy1:hit(damage, r) | |
self.sx, self.sy = 1.35, 1.35 | |
self.hit_flash = true | |
self.timer:tween(0.1, self, {sx = 1, sy = 1}, linear, function() self.hit_flash = false end, "hit") | |
self.hp = self.hp - damage | |
if self.hp <= 0 then | |
self.dead = true | |
table.insert(effects, DeathCircle(self.x, self.y, 2*self.w)) | |
for i = 1, 4 do table.insert(effects, DustParticle(self.x, self.y, white)) end | |
if self.fast then | |
for i = 1, player.max_hp do | |
if hp_bars[i].fake_spent then | |
hp_bars[i]:refresh() | |
break | |
end | |
end | |
end | |
end | |
end | |
DamageNumber = Class:extend() | |
function DamageNumber:new(x, y, vx, vy, t) | |
self.x, self.y = x, y | |
self.sx, self.sy = 1.00, 1.00 | |
self.vx, self.vy = vx, vy | |
self.t = t | |
self.scale_spring = Spring(1) | |
self.scale_spring:pull(0.25) | |
self.r = 0 | |
-- if vx > 0 then self.vr = rng:float(2*math.pi, 4*math.pi) else self.vr = rng:float(-4*math.pi, -2*math.pi) end | |
timer:after(0.25, function() | |
timer:tween(0.05, self, {sx = 0, sy = 0}, linear, function() self.dead = true end) | |
end) | |
end | |
function DamageNumber:update(dt) | |
self.scale_spring:update(dt) | |
self.vy = self.vy + 400*dt | |
self.x = self.x + self.vx*dt | |
self.y = self.y + self.vy*dt | |
-- self.r = self.r + self.vr*dt | |
end | |
function DamageNumber:draw() | |
push(self.x, self.y, self.r, 1.25*self.scale_spring.x*self.sx, 1.25*self.scale_spring.x*self.sy) | |
draw_text(self.t, self.x, self.y, 0, 1, 1, font_small) | |
pop() | |
end | |
HitEffect = Class:extend() | |
function HitEffect:new(x, y) | |
self.x, self.y = x, y | |
self.r = rng:float(0, 2*math.pi) | |
self.animation = Animation(0.025, get_animation_frames("hit1"), "once", {[0] = function() self.dead = true end}) | |
self.sx, self.sy = 1.2, 1.2 | |
timer:tween(0.025*get_animation_frames("hit1"), self, {sx = 1, sy = 1}, linear) | |
end | |
function HitEffect:update(dt) | |
self.animation:update(dt) | |
end | |
function HitEffect:draw() | |
draw_animation("hit1", self.animation:get_current_frame(), self.x, self.y, self.r, 1.35*self.sx, 1.35*self.sy) | |
end | |
DustParticle = Class:extend() | |
function DustParticle:new(x, y, color) | |
self.x, self.y = x, y | |
self.animation = Animation(1, get_animation_frames("smoke1"), "once") | |
self.color = color or red | |
self.sx, self.sy = 0, 0 | |
self.v = rng:float(100, 220) | |
self.r = rng:float(0, 2*math.pi) | |
self.rs = 0 | |
self.vr = rng:float(0, 2*math.pi) | |
timer:after(0.1, function() self.color = color or white end) | |
timer:tween(rng:float(0.04, 0.06), self, {sx = 0.7, sy = 0.7}, cubic_in_out, function() | |
timer:tween(rng:float(0.3, 0.4), self, {sx = 0, sy = 0, v = 0}, linear, function() self.dead = true end) | |
end) | |
end | |
function DustParticle:update(dt) | |
self.animation:update(dt) | |
self.rs = self.rs + self.vr*dt | |
self.x, self.y = self.x + self.v*math.cos(self.r)*dt, self.y + self.v*math.sin(self.r)*dt | |
end | |
function DustParticle:draw() | |
g.setShader(combine) | |
g.setColor(self.color) | |
draw_animation("smoke1", self.animation:get_current_frame(), self.x, self.y, self.rs, 3.5*self.sx, 3.5*self.sy) | |
g.setShader() | |
g.setColor(white) | |
end | |
AnimatedEffect = Class:extend() | |
function AnimatedEffect:new(x, y, name, delay, loop_mode) | |
self.x, self.y = x, y | |
self.r, self.sx, self.sy = 0, 1, 1 | |
self.name = name | |
self.delay = delay | |
self.loop_mode = loop_mode or "once" | |
self.animation = Animation(delay, get_animation_frames(name), self.loop_mode, {[0] = function() self.dead = true end}) | |
end | |
function AnimatedEffect:update(dt) | |
self.animation:update(dt) | |
end | |
function AnimatedEffect:draw() | |
draw_animation(self.name, self.animation:get_current_frame(), self.x, self.y, self.r, self.sx, self.sy) | |
end | |
EllipseParticle = Class:extend() | |
function EllipseParticle:new(x, y, r, v) | |
self.x, self.y = x, y | |
self.r = r | |
self.v = v | |
self.w, self.h = 9, 3 | |
timer:tween({0.2, 0.5}, self, {v = 0}, linear, function() self.dead = true end) | |
end | |
function EllipseParticle:update(dt) | |
self.x = self.x + self.v*math.cos(self.r)*dt | |
self.y = self.y + (self.v*math.sin(self.r) + 100)*dt | |
self.w = remap(self.v, 0, 400, 0, 9) | |
self.h = remap(self.v, 0, 400, 0, 3) | |
end | |
function EllipseParticle:draw() | |
push(self.x, self.y, math.atan2(self.v*math.sin(self.r) + 100, self.v*math.cos(self.r))) | |
ellipsef(self.x, self.y, self.w, self.h) | |
pop() | |
end | |
DeathCircle = Class:extend() | |
function DeathCircle:new(x, y, r, color) | |
self.x, self.y = x, y | |
self.r = r | |
self.color = color or white | |
timer:tween(0.26, self, {r = 0}, cubic_in_out, function() self.dead = true end) | |
end | |
function DeathCircle:update(dt) | |
end | |
function DeathCircle:draw() | |
circlef(self.x, self.y, self.r, self.color) | |
end | |
DeathParticle = Class:extend() | |
function DeathParticle:new(x, y, r, v, color) | |
self.x, self.y = x, y | |
self.r = r | |
self.v = v | |
self.w, self.h = 14, 4.5 | |
self.color = color or white | |
timer:tween({0.2, 0.4}, self, {v = 0}, linear, function() self.dead = true end) | |
end | |
function DeathParticle:update(dt) | |
self.x = self.x + self.v*math.cos(self.r)*dt | |
self.y = self.y + (self.v*math.sin(self.r) + 0)*dt | |
self.w = remap(self.v, 0, 400, 0, 14) | |
self.h = remap(self.v, 0, 400, 0, 4.5) | |
end | |
function DeathParticle:draw() | |
push(self.x, self.y, math.atan2(self.v*math.sin(self.r) + 0, self.v*math.cos(self.r))) | |
rectf(self.x, self.y, self.w, self.h, nil, nil, self.color) | |
pop() | |
end | |
ShootCircle = Class:extend() | |
function ShootCircle:new(x, y, rs) | |
self.x, self.y = x, y | |
self.rs = rs or 12 | |
timer:tween(0.1, self, {rs = 0}, linear, function() self.dead = true end) | |
end | |
function ShootCircle:update(dt) | |
end | |
function ShootCircle:draw() | |
circlef(self.x, self.y, self.rs) | |
end | |
ShootCapsule = Class:extend() | |
function ShootCapsule:new(x, y, r, v) | |
self.x, self.y = x, y | |
self.vx, self.vy = v*math.cos(r), v*math.sin(r) | |
self.w, self.h = 6, 3 | |
self.r = 0 | |
self.vr = rng:float(-4*math.pi, 4*math.pi) | |
end | |
function ShootCapsule:update(dt) | |
self.vy = self.vy + 600*dt | |
self.x = self.x + self.vx*dt | |
self.y = self.y + self.vy*dt | |
self.r = self.r + self.vr*dt | |
if self.y > gh or self.y < 0 or self.x > gw or self.x < 0 then | |
self.dead = true | |
table.insert(effects, ShootCircle(self.x, self.y)) | |
end | |
end | |
function ShootCapsule:draw() | |
push(self.x, self.y, self.r) | |
rectf(self.x, self.y, self.w, self.h) | |
pop() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment