Skip to content

Instantly share code, notes, and snippets.

@utkarshkukreti
Forked from juaxix/Alien.lua
Created August 2, 2014 11:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save utkarshkukreti/5cc23142531e08ac16d9 to your computer and use it in GitHub Desktop.
Save utkarshkukreti/5cc23142531e08ac16d9 to your computer and use it in GitHub Desktop.
Alien = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Alien:init(avoidy)
self.position = vec2(0,math.max(math.abs(math.random(HEIGHT)-avoidy)),66)
self.angle = math.pi
self.points= 500
end
function Alien:draw()
self.position.x = self.position.x + 1.66
sprite("Small World:Base Large",self.position.x,self.position.y)
end
function Alien:touched(touch)
-- Codify does not automatically call this method
end
Asteroid = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Asteroid:init(avoidpos)
self.position = vec2(math.random(WIDTH),math.random(HEIGHT)) - avoidpos
self.angle = math.random(360)
local r = math.random(1,7)
if r == 1 then
self.speed = vec2(1,1)
elseif r == 2 then
self.speed = vec2(1,0)
elseif r ==3 then
self.speed = vec2(0,1)
elseif r == 5 then
self.speed = vec2(-1,-1)
elseif r == 6 then
self.speed = vec2(-1,0)
else
self.speed = vec2(0,-1)
end
self.points = math.random(333)
end
function Asteroid:update()
self.angle = self.angle + 1
self.position = self.position + self.speed
if self.position.x > WIDTH then
self.position.x = 0
elseif self.position.x < 0 then
self.position.x = WIDTH
end
if self.position.y > HEIGHT then
self.position.y = 0
elseif self.position.y < 0 then
self.position.y = HEIGHT
end
end
function Asteroid:draw()
self:update()
pushMatrix()
translate(self.position.x,self.position.y)
rotate(self.angle)
sprite("Planet Cute:Rock")
popMatrix()
end
function Asteroid:touched(touch)
-- Codify does not automatically call this method
end
Cosmos = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Cosmos:init()
self.asteroids = {}
self.loots = {}
self.aliens = {}
self.ship = Ship()
self.stars = Stars()
self.explosions= {}
self.ship.position = vec2(WIDTH/2,HEIGHT/2)
self.maxAsteroids = 6
self.nasteroids = 0
self.AspawnSpeed = 1/11
self.timeNextAst = 0
self.maxAliens = 3
self.naliens = 0
self.alSpawnSpeed = 1/60
self.timeNextAlien= 3
self.maxLoots = 3
self.timeNextLoot = 0
self.nloots = 0
self.loSpawnSpeed = 1/66
end
function Cosmos:generateNextLoot()
self.timeNextLoot = self.timeNextLoot - self.loSpawnSpeed
if self.timeNextLoot<= 0 then
self.timeNextLoot = 9
if self.nloots < self.maxLoots then
table.insert(self.loots,Loot(self.ship.position.y))
self.nloots = self.nloots + 1
end
end
end
function Cosmos:generateNextAlien()
self.timeNextAlien = self.timeNextAlien - self.alSpawnSpeed
if self.timeNextAlien<= 0 then
self.timeNextAlien = 11
if self.naliens < self.maxAliens then
table.insert(self.aliens,Alien(self.ship.position.y))
self.naliens = self.naliens + 1
end
end
end
function Cosmos:generateNextAsteroid()
self.timeNextAst = self.timeNextAst - self.AspawnSpeed
if self.timeNextAst<= 0 then
self.timeNextAst = 6
if self.nasteroids < self.maxAsteroids then
table.insert(self.asteroids,Asteroid(self.ship.position))
self.nasteroids = self.nasteroids + 1
end
end
end
function Cosmos:draw()
self:generateNextAsteroid()
self:generateNextAlien()
self:generateNextLoot()
self.stars:draw()
for i,a in ipairs(self.asteroids) do
a:draw()
end
if self.ship:isDead() then
self.ship:drawDead()
drawText("Game over! ",2,1,50,HEIGHT/2)
drawText("Hit restart! ",1,1,55,(HEIGHT/2)-33)
else
self.ship:draw()
end
for i,a in ipairs(self.aliens) do
if a.position.x> WIDTH then
table.remove(self.aliens, i)
self.naliens = self.naliens - 1
else
a:draw()
end
end
for i,l in ipairs(self.loots) do
l:draw()
end
for i,e in ipairs(self.explosions) do
if e:isDone() then
table.remove(self.explosions, i)
else
e:draw()
end
end
end
function Cosmos:isCollidingWithAliens(o,min,attack)
for i,a in ipairs(self.aliens) do
if a.position:dist(o.position)<min then
if attack then
table.insert(self.explosions,Explosion(a.position,o.acolor,a.points))
table.remove(self.aliens, i)
self.naliens = self.naliens - 1
score = score + a.points
end
return true
end
end
return false
end
-- O(n)
function Cosmos:isCollidingWithAsteroids(o,min,attack)
-- asteroid
for i,a in ipairs(self.asteroids) do
if (a.position:dist(o.position))<min then
if attack then
table.insert(self.explosions,Explosion(a.position,o.acolor,a.points))
table.remove(self.asteroids, i)
self.nasteroids = self.nasteroids - 1
score = score + a.points
end
return true
end
end
end
function Cosmos:touched(touch)
self.ship:touched(touch)
end
Explosion = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Explosion:init(pos,ecolor,points)
self.position = pos
self.opacity = 255
self.time = 0
self.lines = {}
self.ecolor= ecolor
sound("explode",967)
self.text = points
for i = 1,10 do
dir = vec2(0,1)
dir = dir:rotate( math.rad(math.random(360)) )
table.insert( self.lines, dir*math.random(2,7) )
end
end
function Explosion:isDone()
return self.opacity <= 0
end
function Explosion:draw()
self.time = self.time + 0.5
pushStyle()
lineCapMode(ROUND)
strokeWidth(20)
smooth()
local e = self.ecolor
stroke(e.r,e.g,e.b,math.max(self.opacity,0))
local p = self.position
for i,v in ipairs(self.lines) do
vt = p + v * self.time
line(p.x, p.y, vt.x, vt.y)
end
self.opacity = 255 * (1 - (self.time/30))
popStyle()
drawText(""..self.text,2,1,p.x,p.y)
end
Fire = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Fire:init(pos, dmg, acolor)
-- you can accept and set parameters here
self.position = pos
self.speed = vec2(6,0)
self.power = dmg
self.thinkSpd = 666
self.think = 6
self.life = 111
self.acolor = acolor
end
function Fire:update()
self.think = self.think - self.thinkSpd
if self.think <= 0 then
self.position = self.position + self.speed
if self.position.x>WIDTH then
self.position.x = 0
end
if self.position.y <0 then
self.position.y = HEIGHT
elseif self.position.y > HEIGHT then
self.position.y = 0
end
self.think = 6
self.life = self.life - 1
end
end
function Fire:draw()
self:update()
pushMatrix()
translate(self.position.x,self.position.y)
rotate(90)
tint(self.acolor.r,self.acolor.g,self.acolor.b)
sprite("Small World:Raindrop Soft")
noTint()
popMatrix()
end
Font = class()
-- - The Hershey Fonts were originally created by Dr.
-- A. V. Hershey while working at the
-- U. S. National Bureau of Standards.
-- Useful Links:
-- http://emergent.unpythonic.net/software/hershey
-- http://paulbourke.net/dataformats/hershey/
-- Re-encoding of font information and other shenanigans
-- by Tom Bortels bortels@gmail.com November 2011
-- all rights reversed (Hail Eris!)
-- "If I have seen a little further it is by standing
-- on the shoulders of Giants."
-- Isaac Newton
function Font:init()
-- font data - 2 decimal character # of points,
-- followed by 2*points of point data
-- 9->-9, 8-<-8, ... 1->-1, 0->0, A->1, B->2, ... Z->26
self.code = "9876543210ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-- this is the Hershey Roman Simplex font for ascii 32-127
self.fontdata =
"00160810EUEG11EBDAE0FAEB0516DUDN11LULN1121KYD711QYJ711DLRL11"
.. "CFQF2620HYH411LYL411QROTLUHUETCRCPDNEMGLMJOIPHQFQCOAL0H0EACC"
.. "3124UUC011HUJSJQIOGNENCPCRDTFUHUJTMSPSSTUU11QGOFNDNBP0R0TAUC"
.. "UESGQG3426WLWMVNUNTMSKQFOCMAK0G0EADBCDCFDHEILMMNNPNRMTKUITHR"
.. "HPIMKJPCRAT0V0WAWB0710ESDTEUFTFREPDO1014KYIWGTEPDKDGEBG2I5K7"
.. "1014CYEWGTIPJKJGIBG2E5C70816HUHI11CRML11MRCL0526MRM011DIVI08"
.. "10FAE0DAEBFAF1E3D40226DIVI0510EBDAE0FAEB0222TYB71720IUFTDQCL"
.. "CIDDFAI0K0NAPDQIQLPQNTKUIU0420FQHRKUK01420DPDQESFTHULUNTOSPQ"
.. "POOMMJC0Q01520EUPUJMMMOLPKQHQFPCNAK0H0EADBCD0620MUCGRG11MUM0"
.. "1720OUEUDLEMHNKNNMPKQHQFPCNAK0H0EADBCD2320PROTLUJUGTEQDLDGEC"
.. "GAJ0K0NAPCQFQGPJNLKMJMGLEJDG0520QUG011CUQU2920HUETDRDPENGMKL"
.. "NKPIQGQDPBOAL0H0EADBCDCGDIFKILMMONPPPROTLUHU2320PNOKMIJHIHFI"
.. "DKCNCODRFTIUJUMTORPNPIODMAJ0H0EADC1110ENDMELFMEN11EBDAE0FAEB"
.. "1410ENDMELFMEN11FAE0DAEBFAF1E3D40324TRDIT00526DLVL11DFVF0324"
.. "DRTID02018CPCQDSETGUKUMTNSOQOONMMLIJIG11IBHAI0JAIB5527RMQOOP"
.. "LPJOINHKHHIFKENEPFQH11LPJNIKIHJFKE11RPQHQFSEUEWGXJXLWOVQTSRT"
.. "OULUITGSEQDOCLCIDFEDGBIAL0O0RATBUC11SPRHRFSE0818IUA011IUQ011"
.. "DGNG2321DUD011DUMUPTQSRQROQMPLMK11DKMKPJQIRGRDQBPAM0D01821RP"
.. "QROTMUIUGTERDPCMCHDEECGAI0M0OAQCRE1521DUD011DUKUNTPRQPRMRHQE"
.. "PCNAK0D01119DUD011DUQU11DKLK11D0Q00818DUD011DUQU11DKLK2221RP"
.. "QROTMUIUGTERDPCMCHDEECGAI0M0OAQCRERH11MHRH0822DUD011RUR011DK"
.. "RK0208DUD01016LULEKBJAH0F0DACBBEBG0821DUD011RUDG11ILR00517DU"
.. "D011D0P01124DUD011DUL011TUL011TUT00822DUD011DUR011RUR02122IU"
.. "GTERDPCMCHDEECGAI0M0OAQCRESHSMRPQROTMUIU1321DUD011DUMUPTQSRQ"
.. "RNQLPKMJDJ2422IUGTERDPCMCHDEECGAI0M0OAQCRESHSMRPQROTMUIU11LD"
.. "R21621DUD011DUMUPTQSRQROQMPLMKDK11KKR02020QROTLUHUETCRCPDNEM"
.. "GLMJOIPHQFQCOAL0H0EACC0516HUH011AUOU1022DUDFECGAJ0L0OAQCRFRU"
.. "0518AUI011QUI01124BUG011LUG011LUQ011VUQ00520CUQ011QUC00618AU"
.. "IKI011QUIK0820QUC011CUQU11C0Q01114DYD711EYE711DYKY11D7K70214"
.. "0UN31114IYI711JYJ711CYJY11C7J71016FOHRJO11CLHQML11HQH0021602"
.. "P20710FUETDRDPEOFPEQ1719ONO011OKMMKNHNFMDKCHCFDCFAH0K0MAOC17"
.. "19DUD011DKFMHNKNMMOKPHPFOCMAK0H0FADC1418OKMMKNHNFMDKCHCFDCFA"
.. "H0K0MAOC1719OUO011OKMMKNHNFMDKCHCFDCFAH0K0MAOC1718CHOHOJNLMM"
.. "KNHNFMDKCHCFDCFAH0K0MAOC0812JUHUFTEQE011BNIN2219ONO2N5M6K7H7"
.. "F611OKMMKNHNFMDKCHCFDCFAH0K0MAOC1019DUD011DJGMINLNNMOJO00808"
.. "CUDTEUDVCU11DND01110EUFTGUFVEU11FNF3E6C7A70817DUD011NNDD11HH"
.. "O00208DUD01830DND011DJGMINLNNMOJO011OJRMTNWNYMZJZ01019DND011"
.. "DJGMINLNNMOJO01719HNFMDKCHCFDCFAH0K0MAOCPFPHOKMMKNHN1719DND7"
.. "11DKFMHNKNMMOKPHPFOCMAK0H0FADC1719ONO711OKMMKNHNFMDKCHCFDCFA"
.. "H0K0MAOC0813DND011DHEKGMINLN1717NKMMJNGNDMCKDIFHKGMFNDNCMAJ0"
.. "G0DACC0812EUEDFAH0J011BNIN1019DNDDEAG0J0LAOD11ONO00516BNH011"
.. "NNH01122CNG011KNG011KNO011SNO00517CNN011NNC00916BNH011NNH0F4"
.. "D6B7A70817NNC011CNNN11C0N03914IYGXFWEUESFQGPHNHLFJ11GXFVFTGR"
.. "HQIOIMHKDIHGIEICHAG0F2F4G611FHHFHDGBFAE1E3F5G6I70208DYD73914"
.. "EYGXHWIUISHQGPFNFLHJ11GXHVHTGRFQEOEMFKJIFGEEECFAG0H2H4G611HH"
.. "FFFDGBHAI1I3H5G6E72324CFCHDKFLHLJKNHPGRGTHUJ11CHDJFKHKJJNGPF"
.. "RFTGUJUL"
local i=1
local c=32
self.font = {}
while (i < string.len(self.fontdata)) do
local cs = string.char(c)
self.font[cs] = {}
local points = string.sub(self.fontdata, i, i+1)
self.font[cs].points = points
self.font[cs].char = cs
self.font[cs].ascii = c
self.font[cs].width = string.sub(self.fontdata, i+2, i+3)
i = i + 4
self.font[cs].data = string.sub(self.fontdata, i, i+points*2)
i = i + points*2
c = c + 1
end
i=-9
self.decode = {}
for c in self.code:gmatch"." do
self.decode[c]=i
i=i+1
end
end
-- returns width in pixels of unscaled, strokeWidth(1) string
function Font:stringwidth(s)
local x, l, i = 0, string.len(s)
for i = 1, l do
x = x + self.font[s:sub(i, i)].width
end
return x
end
-- draw a string at x,y (skipping offscreen draws)
function Font:drawstring(s, x, y)
local l, i
l = string.len(s)
for i = 1, l do
local c = s:sub(i, i)
local w = self.font[c].width
if ((x + w) >= 0) then
x = x + (self:drawchar(c, x, y))
else
x = x + w -- skip offscreen left (but track position)
end
if (x > WIDTH) then break end -- skip offscreen right
end
end
-- optimized draw string at x,y (old version for reference)
function Font:olddrawstring(s, x, y)
local l, i
l = string.len(s)
for i = 1, l do
x = x + (self:drawchar(string.sub(s, i, i), x, y))
end
end
function Font:drawchar(c, x, y)
local ax, ay, bx, by, minx, maxx = -1, -1, -1, -1, -1, -1
local p, plot
local ch = self.font[c]
for p=1, ch.points do
ax=bx
ay=by
bx=self.decode[ch.data:sub(p*2-1, p*2-1)]
by=self.decode[ch.data:sub(p*2, p*2)]
plot=true
if ((ax==-1) and (ay==-1)) then plot=false end
if ((bx==-1) and (by==-1)) then plot=false end
if (plot) then
line(x+ax, y+ay, x+bx, y+by)
end
end
return ch.width -- for drawstring
end
Loot = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Loot:init(avoidy)
if math.random(66)>33 then
self.type = "life"
self.life = math.random(1,45)
else
self.type = "powerup"
local r = math.random(1,3)
if r == 1 then
self.acolor = color( 255, 253, 0, 255 )
elseif r == 2 then
self.acolor = color(255, 0, 0, 255)
elseif r == 3 then
self.acolor = color(0, 0, 255, 255)
end
end
self.position = vec2(WIDTH,math.max(math.abs(math.random(HEIGHT)-avoidy)),66)
self.collected = false
self.rise = 0
end
function Loot:collect(point)
if self.collected then return false end
dtg = point:dist(self.position)
if dtg < 50 and not self.collected then
sound(SOUND_PICKUP, 195)
self.collected = true
return true
end
return false
end
function Loot:draw()
self.position.x = self.position.x - 1.6
pushMatrix()
translate(self.position.x, self.position.y + self.rise)
if self.type == "powerup" then
tint(self.acolor)
sprite("Small World:Mote Sad")
noTint()
else
sprite("Small World:Mote Happy")
end
popMatrix()
if self.collected then
self.rise = self.rise + 15
end
end
-- Main
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function setup()
cosmos = Cosmos()
score = 0
watch("score")
f=Font()
frame=1
end
function drawText(text,sw,sc,x,y)
stroke(255, 255, 255, 255)
noSmooth()
strokeWidth(sw)
lineCapMode(ROUND)
scale(sc)
f:drawstring(text, x, y)
end
function draw()
background(0)
cosmos:draw()
if frame < 333 then
frame = frame + 1
drawText("Game Start! ",2,1,50,HEIGHT/2)
end
end
function touched(touch)
cosmos:touched(touch)
end
Ship = class()
-- Free game by juaxix
-- http://www.xixgames.com
-- Copyright LGPL - 11/2011
function Ship:init()
-- you can accept and set parameters here
self.position = vec2(0,0)
self.size = 66
self.health = 50
self.attackPower = 1
self.attacks = {}
self.invulnDuration = 0
self.knockVel = vec2(0,0)
self.maxattacks = 3
self.nattacks = 0
watch("ShipHealth")
self.acolor = color(255, 253, 0, 255)
end
function Ship:move(dir)
newPos = self.position + dir * 20
newPos = newPos + self.knockVel
if cosmos:isCollidingWithAsteroids(self,45,false) or
cosmos:isCollidingWithAliens(self,66,false) then
-- hit
self:applyDamageFromPoint(self.position,math.random(3,6))
else
if newPos.x > WIDTH then
newPos.x = 0
elseif newPos.x < 0 then
newPos.x = WIDTH
end
if newPos.y > HEIGHT then
newPos.y = 0
elseif newPos.y < 0 then
newPos.y = HEIGHT
end
self.position = newPos
end
-- check loots
for i,l in ipairs(cosmos.loots) do
if l:collect(self.position) then
if l.type == "powerup" then
self.acolor = l.acolor
self.maxattacks = math.min(self.maxattacks + 1, 21)
else
self.health = math.min(l.life+self.health,50)
end
end
if l.position.y>HEIGHT or l.position.x<0 then
table.remove(cosmos.loots, i)
cosmos.nloots = cosmos.nloots - 1
end
end
end
function Ship:applyDamageFromPoint(point, damage)
if self.health == 0 then return end
if self.invulnDuration == 0 then
sound(SOUND_HIT)
self.health = math.max(self.health - damage, 0)
if self.health == 0 then
table.insert(cosmos.explosions, Explosion:init(self.position,color(255,255,255,255),"Died") )
end
local l = self.position - point
l = l:normalize()
self.invulnDuration = 0.5
end
end
function Ship:fire()
if self.nattacks<self.maxattacks then
sound(SOUND_HIT,123)
table.insert(self.attacks, Fire(self.position, self.attackPower,self.acolor) )
self.nattacks = self.nattacks + 1
end
end
function Ship:damageAtPoint(point)
-- if self.currentAttack then
-- dta = self.position:dist(point)
-- if dta < self.currentAttack.currentSize * 0.4 then
-- return dta/self.currentAttack.blastSize * 30
-- end
-- end
return 0
end
function Ship:isDead()
return self.health == 0
end
function Ship:drawDead()
-- tint(50,50,50,255)
sprite("Small World:Bunny Skull", self.position.x + 5, self.position.y + 25, self.size + 20)
end
function Ship:computeHeadAngle()
pushMatrix()
translate(WIDTH/2, HEIGHT/2)
grav = vec2(Gravity.x * 300, Gravity.y * 300)
--print(grav)
line(0, 0, grav.x, grav.y)
-- Arrowhead
down = vec2(0,-1)
orient = down:angleBetween(grav)
pushMatrix()
resetMatrix()
translate(WIDTH/2,HEIGHT/2)
translate(grav.x,grav.y)
rotate(math.deg(orient))
line(0, 0, -20, 25)
line(0, 0, 20, 25)
popMatrix()
-- End Arrowhead
popMatrix()
return math.deg(orient)
end
function Ship:draw()
self.invulnDuration = math.max(self.invulnDuration - 1/60, 0)
self.angle = self:computeHeadAngle()
moveVec = vec2(Gravity.x, Gravity.y) + vec2(0,0.6)
self:move(moveVec)
pushStyle()
for i,a in ipairs(self.attacks) do
a:draw()
if cosmos:isCollidingWithAsteroids(a, 48, true) or
cosmos:isCollidingWithAliens(a,45,true)
or a.life<=0
then
table.remove(self.attacks,i)
self.nattacks = self.nattacks - 1
end
end
stroke(0,0,0,0)
fill(15, 23, 65, 141)
ellipse(self.position.x + 5, self.position.y - 5, self.size, self.size)
self:tintForInvulnDuration(self.invulnDuration)
pushMatrix()
translate(self.position.x + 5, self.position.y)
rotate(self.angle)
sprite("SpaceCute:Rocketship", 0,0 + 25, self.size + 20)
popMatrix()
popStyle()
noTint()
ShipHealth = self.health
end
function Ship:touched(touch)
if self.health == 0 then return end
if touch.state == ENDED then --and self.invulnDuration == 0 then
self:fire()
end
end
function Ship:tintForInvulnDuration(inv)
if inv > 0 then
local flashAmount = (math.sin(inv*20)+1)*0.5
tint(255,255,255,255 * flashAmount)
else
--tint(255,255,255,255)
noTint()
end
end
-- This class draws the lines streaming past in the background
-- of the game. We spawn and delete them in the self.lines table
-- by Simeon - Codea
----------------------------------------------
-- Star
----------------------------------------------
Star = class()
function Star:init(pos, vel)
self.position = pos
self.velocity = vel
end
function Star:update()
self.position.x = self.position.x - self.velocity
end
function Star:draw()
p = self.position
line(p.x-self.velocity,p.y,p.x,p.y)
end
function Star:shouldCull()
-- Check if off the left of the screen
if (self.position.x - self.velocity) < 0 then
return true
end
return false
end
----------------------------------------------
-- All stars
----------------------------------------------
Stars = class()
function Stars:init()
self.minSpeed = 6
self.speed = 23
self.spawnRate = 1
self.stars = {}
end
function Stars:updateAndCull()
toCull = {}
for i,star in ipairs(self.stars) do
if star:shouldCull() then
table.remove( self.stars, i )
else
star:update()
end
end
end
function Stars:update()
-- Create spawnRate lines per update
for i = 1,self.spawnRate do
-- Generate random spawn location
vel = math.random(self.minSpeed, self.speed)
spawn = vec2( WIDTH - vel, math.random(HEIGHT) )
table.insert(self.stars, Star(spawn, vel))
end
-- Update and cull offscreen lines
self:updateAndCull()
end
function Stars:draw()
self:update()
pushStyle()
noSmooth()
stroke(179, 153, 180, 173)
strokeWidth(2)
lineCapMode(SQUARE)
for i,star in ipairs(self.stars) do
star:draw()
end
popStyle()
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment