epfPfPWffuepPfPPf  >C4CDNvw`w`hwwv`w``w UUUeeffvewwveveveveffvvwwwUfVfUgeUuVeVgefuvw_VepUfgUffwWUUvgUfgUffwgUfoUfoUveoeWeovevoWfvvwwvvevevUeUeUeUefffvfWgvUfwUUUeeffveUeveevveevveveffvvwwwwpvevVeUeUeUeevevfvevpvvvevevUeUeUeUeevevevevvvUwwUfwUVueUeVegegvgWvvUffwvvevevUeUuUeUeevefevevvvfUvuUgvUeUeUeUeevevevevvvVUUvgUfgUffwVUUvgUfgUffw p`vgw `pphppwf ``p`x`fgUUeUfgUfVfwUgUUgUfvUfvfwwwgffwVUvuVeWvfvevfWevUevfwVUUvgUfgUffwWUUvgUfpUfwwwefvgWUegvUegeWvgUvuoUwfwVUUvvUfUUfUfwvUUvgUfgUffwUUUeeffveD4ve4#ve4#veveffvvwwwPpDDD4433#4""#4#4#4#433##"""D3!C3D24D$C4C243$#"AC4 D32D3!3"BDD#2D32D3!3"2D31D31D#414B41#4#1B3#!#""##4#4#D4D4D4D4333#3B2#D!3"DDD4433#4D4#44##44##4#433##"""" #4#C4D4D4D44#4#3#4# ###4#4#D4D4D4D44#4#4#4###!D""D3"DC$4D4C4242#2B##!D3!3"##4#4#D4D$D4D44#434#4###3D#$D2#D4D4D4D44#4#4#4###CDD#2D32D3!3"CDD#2D32D3!3" 0#2@@"0 1  @@"300 0!0@@3C4C43D3D4C4CD3D3C4C43D3D4C4CD3D3C4C4@00@@4C4CD3D32DD4!D32!D3C3"D2DD2!D3#!D3#3"""233"CD#$C4B#3#4#3B4#!D4#3"CDD#2D32D3!3"BDD#2D3 D3!!"""43#2BD42#D424B#2D#$1D"!3"CDD##D3DD3D3"#DD#2D32D3!3"DDD4433#4Ue#4ev#4ev#4#433##"""@       3### PUPPUPUUPUUUUPUUUUPPPUUUUUUUPPPUUUUPUUPPUUPPUUPPPPUPPUPPUUPPPPUPPUPPPUPPUPUPPUPPUUUUPPUPUPPPPPPPPPP@ D"@ P UEP`@2dB2d$L2d@ ffe` ffUUPuWUWuUUwwUWWuuwUUwpwwwwpUUWuWuUWuUUwwUWWuupUU"" """"!"!""""!""""!"!""`    ʬ˻ РР DD@B$D""DDD$DB"B$"DD" """" DDBB$$D""DDD$DB"B$ DD`p`P P""!P PPPP DDNNDNNDDDDDC I94`VU`WVW`uveUYYYuuegW ! !ݝݩݝ  ppfgffYPgV`pe`efivffevV`Vp  - = " -" -""-2234404@4 ##3CCCCCD343334444D34C4DC43CD3CC!CC3CC33C!C4!C2D !CD NC DC@4D#D4L44DVVgUVg`UVuPeVuPU]`VUveeveUWeUWeV\U\\mUe - " ""!"!"""""!"ݝݙݝ  `Pp`p`gp`gWp`we`gppPpPvuvVpwv}  , - "C4 40$D@40D0D0C4CADBICIDD CC4433C34!"#2"!333!44!D3#22""22#          ȪȪȊ̪̊ ̼̻̈̀  ̌Ȭ ̫ ̈ʌ Эݭ   ̪ȪȠȠ ̼   Ȭʌ̈ Э   ̐ ɰ ̻ 3#D4D4D43#3#3#3#3#3#"""""3#3#3#3#3#PEV`TeDDVEeT@@@@4B C$33B4$C000 2!#33!2#000 ``ffefffeffvUeVuUuUuPuUp``ffefffeffVUgVWUWUWPWUP``ffefffeffVuevUuUuUpUuP``ffgfffgffVWeVUWUWUPUWP 03, 03"0 `f| `fw` @D< @D3@ P\\P`VeVe`pgvgvppp @LL@0C4C40 2#2#    ˼˼  ˼˼ JJ @4C4C@0#2#20 !!    =M"#$"#"#$"#$$"#$$$$"#$$"#$$$"#$"#$$"#$$$"#$$"#$$$"#$$"#$$$$"#232323"#23$$"#23$$"#$23$"#$23$"#$23"#23$$"#23$$"#23$$"#23$$"#23$$"#23$$"#"#23"#23$2323232323$2323$2323$232323$23"#$"#"#232323$$$"#$"#$2323$"#"#$2323"#N;;;N$"#$23O;;<;;OXFFFFFFFFFFFFFFFFFFFFFFFFFFV$23$$;;;;;;;JO~J$$;<;<;<;"#$JO~J$"#;;;;;;;23$JO~J"#$23$O;;<;;OJO~J23"#N;;;NJO~J$$23JO~J"#$$"#XFFFFFFYO~5FFFFV7$23$$$23J;;;;;;JEFFFFFFFV~W;;<;8Z"#$J;<;;;;JZ;;;;;;J~;;O23"#$J;;JZ;;<;;J~{{O$23J;WO;;;;JZ{{O$"#$EFFFF6OO;;;;JZ5ZNNNE::HO$23$"#{{KKOO;<;;JZ{{O"#$23{{KKOZ;;;;J{{O$23"#{{KKOZ;;;;J;;O$23"#${{KKOO;<;;J7;;<;XZ$$23EFFFFV;OO;;;;J5FFFF6W"#$J;;7O;;;;JJ23"#J;<;;;;JZ;;<;;JJ$$23"#J;;;;;;JZ;;;;;;JJ"#238FFFFFFYEFFFFFFF6J$23$$J~J"#J~J23$"#J~J$$"#23J~J$23$J~J$"#$$J~~J$23"#$J~~J"#23J~~J"#$23$UFFFFFFFFF9HE9FFFFFFFFF923$$"#JK{{KJ;;;J77J;;;JK{{KJ$$238FFFFF6;<;8686;<;8FFFFF6"#$"#2323$$$NNNNN"#~$::::$23"#$"#23"#$2323$"#$$23"#$NNNNN"#23$23$$$"#$$23"#"#$"#232323$$$"#$23"#"#23"#"#232323"#$23"#$$$23$$$"#$"#$23$23"#"#$2323"#$$$23$"#$"#2323"#$$23"#$$$23"#"#2323"#"#"#"#"#"#"#$"#23"#$23$"#23"#$$"#23"#$$"#23"#$$"#23"#$23$$23"#$$23$$"#$23"#$$23$$"#23"#$$23$$"#23"#$$23$$"#$23"#$$23$$"#$$$23$$$$$23$$23$$$$$23$23$$$$$23$23$$$$$23$23$$$$$23$ > ?  D0 D̪@N@C.0@!CL !"3D4 N3DD3 4DDD#"CD4@D4L3  NC@@    wwwowowowowowowowowowowowowowowowowopWffWfWfWfWfWfWfWfWfWfWfWfWfWfWfWfWfWpvUUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVpfWUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVUVpVveWfWfWfWfWfWfWfWfWfWfWfWfWfWfWfWfWpVeWWfWfWfWfWfWfWfWfWfWfWfWfWfWfWfWfW Vuwowowowowowowowowowowowowowowowowo gv`UUeߚ pVevߙ9pVev9#!.gvD33CD`UUe0DDD3pVevCDD4pVev CDLgv C`UUeCpVev 4DpVev DC̻gv D˻ `UUe˻pVev̻pVev gv`UUepVevpVev gv`UUepVevpVevwwwwwwwwwwwwwwwwwwwwwwwwwgvwwwwwwwwwwwwwwwwwwwwwwwww`UUewppppp pVevwppppppVevwppppgvwppppp`UUewppppppVevwppppppVevwpppppgvwpppp!`UUewpp3pr"pVevwwwwwwwwwwwwww=333wwwwww"pVevwpp333p"rgvwpp33"pq`UUewpp#"һq pVevwpp.prpVevwppp/"rgvwpppp"r"`UUewppppp"pVevwppp! pVevwpppp"pgvwwwwwwwwwwwwwwwwwwwwwwww`UUewpppp"pVevwppppppVevwpppppgvwpppppwwwowowowowowowowowowowowowoUUewppppppWffWfWfWfWfWfWfWfWfWfWfWfWfgWevwpppppvUUVUVUVUVUVUVUVUVUVUVUVUVUVUevwppppppfWUVUVUVUVUVUVUVUVUVUVUVUVUVevwppppppVveWfWfWfWfWfWfWfWfWfWfWfWfgWUewppppppVeWWfWfWfWfWfWfWfWfWfWfWfWfwVevwwwwwwwwwwwwwwwwwwwwwwwwwVuwowowowowowowowowowowowowVevwpppppgvgvwppppp`UUegp`p`p`p`p`p`p`p`p`p`p`p`UUewpppppwo pVevpVevwppppgWvWpVev`p`p`p`p`p`p`p`p`p`p`p`pVevwpppp`UVuVgvgvwppppPUVuV`UUegp`p`p`p`p`p`p`p`p`p`p`p`UUewppppPfWvWpVevpVevwppp p`fWvWpVev`p`p`p`p`p`p`p`p`p`p`p`pVevwppppwowogvgvwwwwwwwwwwwwwwwwwwwwwwwww`UUegp`p`p`p`p`p`p`p`p`p`p`p`UUewpppppPpVevpVevwpppppPpVev`p`p`p`p`p`p`p`p`p`p`p`pVevwpppppPgvgvwpppppP`UUegp`p`p`p`p`p`p`p`p`p`p`p`UUewpppppP pVevpVevwpppppPpVev`p`p`p`p`p`p`p`p`p`p`p`pVevwpppppPgvgvwwwwwwwwwwwwwwwwwwwwwwwww``UUegp`p`p`p`UUeP,0,]']>S}Wup8d%qy)6o;]AsVl3=0 and y.bf_y>=0 then if e==y.bf_x and f==y.bf_y then return end;local z=self.broadphase[y.bf_x][y.bf_y]for j,k in ipairs(z)do if k==y then if j~=#z then z[j],z[#z]=z[#z],z[j]end;table.remove(z)break end end end;if y.x>=0 and y.x<=240*8 and y.y>=0 and y.y<=136*8 then table.insert(self.broadphase[e][f],y)y.bf_x,y.bf_y=e,f else y.bf_x,y.bf_y=-1,-1 end end;function Scene:unregisterBF(y)if y==nil then return end;if y.bf_x and y.bf_y and y.bf_x>=0 and y.bf_y>=0 then local z=self.broadphase[y.bf_x][y.bf_y]for j,k in ipairs(z)do if k==y then if j~=#z then z[j],z[#z]=z[#z],z[j]end;table.remove(z)break end end end end;function Scene:load()end;function Scene:unload()end;function Scene:leave()end;function Scene:enter()end;function Scene:TIC()end;function Scene:OVR()end;function Scene:SCN(d)end;Entity=class('Entity')function Entity:init(g,a)self.scene=nil;self.name=""self.parent=nil;self.children={}self.comps={}self.awake=false;self.death=false;self.name=g;if a~=nil then self.scene=a end;self:onInit()self:setAwake(true)end;function Entity:addComp(A,...)local B=#self.comps+1;self.comps[B]=A:new(...)local C=self.comps[B]C.entity=self;C:onAdded()return self.comps[B]end;function Entity:rmComp(A)for D,k in ipairs(self.comps)do if k==A then k:onRemoved()k.entity=nil;self.comps[D]=nil;return k end end end;function Entity:getComp(E)for D,k in ipairs(self.comps)do if k:isInstanceOf(E)then return k end end;return nil end;function Entity:setAwake(F)if self.awake~=F then self.awake=F;if F then self:onAwake()else self:onAsleep()end end end;function Entity:destroy()self:setAwake(false)for j,k in ipairs(self.comps)do k:destroy()end;self.death=true end;function Entity:update()self:onUpdate()local l={}for D,k in ipairs(self.comps)do if k.awake then k:onUpdate()end;if k.death then l[#l+1]=D end end;for j=#l,1,-1 do ele=table.remove(self.comps,l[j])ele=nil end end;function Entity:draw()self:onDraw()for D,k in ipairs(self.comps)do if k.awake then k:onDraw()end end end;function Entity:onAdded()end;function Entity:onInit()end;function Entity:onAwake()end;function Entity:onAsleep()end;function Entity:onUpdate()end;function Entity:onDraw()end;function Entity:onDeath()end;Component=class('Component')function Component:init(...)self.entity=nil;self.awake=false;self.death=false;self:setAwake(true)self:onInit(...)end;function Component:setAwake(F)if self.awake~=F then self.awake=F;if F then self:onAsleep()else self:onAwake()end end end;function Component:destroy()self:onDeath()if self.entity~=nil then self.entity:rmComp(self)end;self.entity=nil;self.awake=false;self.death=true end;function Component:onInit()end;function Component:onAdded()end;function Component:onRemoved()end;function Component:onAwake()end;function Component:onAsleep()end;function Component:onUpdate()end;function Component:onDraw()end;function Component:onDeath()end Timer={ltick=time(),delta=0,elapsed=0,ltick=0,ntick=0,timers={},size=0,free=1,nextFree=function(self)local a=self.timers;local b=self.free;while a[b]~=nil do b=b+1 end;self.free=b+1;if b>self.size then self.size=b end;return b end,update=function(self)self.elapsed=time()/1000;self.delta=self.elapsed-self.ltick;self.ltick=self.elapsed;for c=1,self.size do local d=self.timers[c]if d then if not d.run then self.timers[c].at=d.at+self.delta elseif self.elapsed>=d.at then d.cb(d.ctx)if d.mode=='once'then self:kill(c)else self.timers[c].at=self.elapsed+d.span end end end end end,reset=function(self,e)local a=self.timers[e]if a then self.timers[e].at=self.elapsed+a.span end end,stop=function(self,e)local a=self.timers[e]if a then self.timers[e].run=false end end,start=function(self,e)local a=self.timers[e]if a then self.timers[e].run=true end end,kill=function(self,e)local a=self.timers[e]if a then self.timers[e]=nil;if e=d.x1 and l<=d.x2 and m>=d.y1 and m<=d.y2 then j(c,k)end end,stop=function(c,g)local d=g or AffectFn.Defaults.stop;c[3],c[4]=0,0 end,speed=function(c,g)local d=g or AffectFn.Defaults.speed;c[3],c[4]=c[3]*d.scale,c[4]*d.scale end,bounce=function(c,g)local d=g or AffectFn.Defaults.speed;c[3],c[4]=-c[3]*d.scale,-c[4]*d.scale end}AffectFn.Defaults={linear={vx=-0.01,vy=0.004},attract={x=120,y=68,radius=50,strength=0.001},zone={x1=0,y1=30,x2=240,y2=70,zoneFn=AffectFn.linear,zoneSett=AffectFn.Defaults.linear},stop={},speed={scale=1.01},bounce={scale=0.8}}PSettings={maxPart=2200,lifetime=40,rate=60,emitCt=50,x={-50,50},y={-50,50},vx={-1,1},vy=-1,r=nil,angle=nil,emitfn=DrawFn.timespr,emitSett={frames={1,2,3,4,5}}}PSystem=class('PSystem')function PSystem:init(g)local d=g or PSettings;self.settings=d;self.pool={}self.count=0;for n=1,self.settings.maxPart do self.pool[n]={0,0,0,0,0,0,0,0,false,nil,nil}end;self.timer=0;self.rate=self.settings.rate;self.next=self.rate;self.x=120;self.y=70;self.bx=self.x;self.by=self.y;self.bound=false;self.affects=d.affectors;self.offsetx=0;self.offsety=0 end;function PSystem:update()self.timer=self.timer+1;local o=self.pool;local h=self.x-self.bx;local i=self.y-self.by;local p=self.count;local q=self.bound;local r=self.affects;for n=1,p do local c=o[n]if c==nil then break end;if c[9]then for s=1,#r do r[s][1](c,r[s][2])end;c[5]=c[1]-c[3]*2;c[6]=c[2]-c[4]*2;c[1]=c[1]+c[3]c[2]=c[2]+c[4]if q then c[1]=c[1]+h;c[2]=c[2]+i end;c[7]=c[7]+1;if c[7]>=c[8]then c[9]=false;if p>1 and n~=p then o[n],o[p]=o[p],o[n]end;p=p-1 end end end;self.count=p;self.bx=self.x;self.by=self.y;self.timer=self.timer+1;if self.timer>self.next then self.timer=0;self:spawn(self.settings.emitCt)self.next=self.rate end end;function PSystem:draw()local o=self.pool;for n=1,#o do local c=o[n]if c[9]then c[10](c,c[11])end end end;function PSystem:spawn(t)local p=t or 1;local o=self.pool;if p>#o-self.count then p=#o-self.count end;if p<1 then return end;local u=self.settings.emitfn;local v=self.settings.emitSett;local w=self.settings.lifetime;local x=self.count;local function y(z)if type(z)==nil then return nil end;if type(z)=='table'then if z[2]==nil then return z[1]end;return rndd(z[1],z[2])end;return z end;for n=1,p do local A,B=self.x,self.y;local C,D=y(self.settings.x),y(self.settings.y)local E,F=y(self.settings.vx),y(self.settings.vy)local G,H=y(self.settings.r),y(self.settings.angle)if G then A=A+math.cos(H)*G;B=B+math.sin(H)*G else A=A+C;B=B+D end;x=x+1;o[x]={A,B,E,F,A,B,0,w,true,u,v}end;self.count=x end FPS={tm=0,ltime=0,frames=0,fps=0,update=function()FPS.frames=FPS.frames+1;local a=time(nil)FPS.tm=FPS.tm+a-FPS.ltime;FPS.ltime=a;if FPS.tm>=1000 then FPS.tm=FPS.tm-1000;FPS.fps=FPS.frames;FPS.frames=0 end end,draw=function()print(FPS.fps,10,20,15)end} FSM=class('FSM')function FSM:init(a)self.state=""self.ctx=a;self.states={}end;function FSM:add(b,c)b.fsm=self;b.ctx=self.ctx;self.states[c]=b end;function FSM:update()if self.state~=nil then self.states[self.state]:update()end end;function FSM:transition(c)if self.state~=""then self.states[self.state].exit()end;self.state=c;self.states[c]:enter()end;State=class('State')function State:enter()end;function State:exit()end;function State:update()end function dot(x1,y1,x2,y2) return (x1 * x2) + (y1 * y2); end function cross(x1,y1,x2,y2) return (x1 * y2) - (y1 * x2); end function magnitude (x,y) if x > y then x,y = y,x end return math.sqrt(x*x + y*y) end -- ~1.4% acc function fmagnitude (x,y) if x > y then x,y = y,x end return y + 0.428 * x * x / y end function normalize(x,y) local mag = magnitude(x,y) if mag == 0 then return 0,0 end return x / mag, y / mag end function dist (x1,y1,x2,y2) return magnitude(x2-x1,y2-y1) end function fdist (x1,y1,x2,y2) return fmagnitude(x2-x1,y2-y1) end function spriteUV (spr) local sry = (spr // 16) local srx = (spr - (sry * 16)) return srx*8,sry*8 end function orthQuad (x1,y1,x2,y2,u1,v1,u2,v2,tilesheet,rot,flip) if flip == 1 then u1,u2 = u2,u1 elseif flip == 2 then v1,v2 = v2,v1 elseif flip == 3 then u1,u2 = u2,u1 v1,v2 = v2,v1 end textri(x1,y1,x1,y2,x2,y1,u1,v1,u1,v2,u2,v1,tilesheet,0) textri(x2,y1,x1,y2,x2,y2,u2,v1,u1,v2,u2,v2,tilesheet,0) end function spriteQuad (spr,x1,y1,x2,y2,rot,flip,width,height) width = width or 1 height = height or 1 local u1,v1 = spriteUV(spr) local u2,v2 = u1+(8 * width),v1+(8 * height) orthQuad(x1,y1,x2,y2,u1,v1,u2,v2,false,rot,flip) end function lerp (a,b,f) return (a * (1.0 - f)) + (b * f); end function coserp(a,b,f) local T=(1-math.cos(f*math.pi))/2 return a*(1-T)+b*T end function angleBetween(x1,y1,x2,y2) return math.atan2(x2 - x1, y2 - y1) - 90; end function eulerBetween (a1,a2) local ia2 = (a2 - a1) % 360 while (ia2 > 360.0 or ia2 < 0.0) do if ia2 > 360.0 then ia2 = ia2 - 360.0 end if ia2 < 0.0 then ia2 = ia2 + 360.0 end end if ia2 > 180.0 then return ia2 - 360.0 end return ia2 end -- Coord in, solid out function solid(x,y) return fget(mget(x//8,y//8),0) end function safe(x,y) return fget(mget(x//8,y//8),2) end function fmtVec (x,y) return string.format('(%.3f,%.3f)',x,y) end function fireBullet (scene,owner,x,y,vx,vy,power) local b = scene:addEnt("bullet") local xf = b:addComp(Transform) local vel = b:addComp(Velocity) xf.x = x xf.y = y vel.x = vx vel.y = vy local rc = b:addComp(RectCollider,-1,-1,2,2,'projectile') rc.bounce_factor = 1 local bul = b:addComp(Bullet) bul.owner = owner if power ~= nil then bul.power = power end return bul end function fireBomb (scene,owner,x,y,vx,vy,power) local bname = "bomb"..time() local b = scene:addEnt(bname) local xf = b:addComp(Transform) local vel = b:addComp(Velocity) xf.x = x xf.y = y vel.x = vx vel.y = vy local rc = b:addComp(RectCollider,-4,-4,8,8,'projectile') rc.bounce_factor = 1 local bmb = b:addComp(Bomb) bmb.owner = owner if power ~= nil then bmb.power = power end return bmb end function rectOverlap(ax1,ay1,ax2,ay2,bx1,by1,bx2,by2) return not (bx1 > ax2 or bx2 < ax1 or by1 > ay2 or by2 < ay1) end function string.starts(String,Start) return string.sub(String,1,string.len(Start))==Start end function rollPalette(amt) PALETTE_MAP = 0x3FF0 blue = 9 red = 2 end Factory = { createShip = function(scene,pilot,input,stype) stype = stype or pilot.ship or 1 trace("Creating "..pilot.name.." as "..stype) local newe = scene:addEnt(pilot.name) newe:addComp(Transform,0,0,0,1) newe:addComp(Velocity) newe:addComp(RectCollider,-8,-8,16,16,'ship') --local sm = math.random(1,#Ships) --newe:addComp(TriRend,256 + sm,2,2) newe:addComp(TriRend,Ships[stype].sprite,2,2) newe:addComp(ShipCtrl,stype) newe:addComp(Mover) newe:addComp(input) -- Input or AI newe:addComp(Exhaust) --newe:addComp(Teleport) return newe end, createContainer = function(scene) local newe = scene:addEnt("Container") newe:addComp(Transform,0,0,0,1) newe:addComp(Velocity) newe:addComp(RectCollider,-4,-4,4,4,'pickup') newe:addComp(Mover) newe:addComp(TriRend,384,1,1) newe:addComp(Container) return newe end } -- Fastest way would be to preload a lookup table function tinyprt (str,x,y) local map = "1234567890-.abcdefghijklmnopqrstuvwxyz+ :!()" local ss = 197 for i=1,#str do local pos = string.find(map,string.sub(str,i,i),1,true) - 1 local idx = pos // 4 local quad = pos - (idx * 4) local xp = x + (i - 1) * 4 local ox,oy = xp,y if quad == 1 or quad == 3 then ox = ox - 4 end if quad > 1 then oy = oy - 4 end clip(xp,y,4,4) spr(ss + idx,ox,oy,0) end clip() end function printc(t,x,y,c,f,s,m) local w=print(t,0,-20)*s print(t,x-(w/2),y,c,f,s,m) end Prefs = { ship = 1, kills = 0, deaths = 0, god = false, enemies = true, debug = false, } AniTiles = { defs = { -- Round glowy things {75,76,77,speed=0.2}, {123,124,125,speed=0.2}, {171,172,173,speed=0.2}, -- Controller buttons {220,221,speed=0.2}, {222,223,speed=0.2}, {236,237,speed=0.2}, {238,239,speed=0.2}, }, list = {}, build = function() for k,v in ipairs(AniTiles.defs) do for ak,av in ipairs(v) do AniTiles.list[av] = k end v.time = 0 v.curr = 1 end end, get = function(tile) local nt = AniTiles.list[tile] if nt then local def = AniTiles.defs[nt] return def[def.curr] end return tile end, update = function(delta) if delta == nil then return end for k,v in ipairs(AniTiles.defs) do v.time = v.time + delta if v.time > v.speed then v.time = 0 v.curr = v.curr + 1 if v.curr > #v then v.curr = 1 end end end end } Ships = { addShip = function(stats) local new = {table.unpack(Ships.defaults)} for k,v in pairs(stats) do new[k] = v end table.insert(Ships,new) end, defaults = { maxenergy = 2600, energy = 2600, alive = true, para = false, boost = false, accel = 0.05, maxspeed = 1, turnspeed = 2, baccel = 0.1, maxbspeed = 2, healnrg = 5, boostnrg = 3, shotpower = 1000, shotenergy = 500, shotspeed = 3, shotttl = 5.0, tripleshot = false, tripleangle = 20, shotcd = 40, bombpower = 2000, bombenergy = 1000, bombspeed = 1.5, bombinertia = true, bombttl = 5.0, shottimer = 0, shaketimer = 0, shakeamt = 0, paratimer = 0, shockcd = 20, shocktimer = 0, shockrad = 40, --target = nil, targrange = 100, safe = false, } } Ships.addShip({ name = "Phoenix", desc = "Sniper", sprite = 256, shotpower = 9001, shotenergy = 1500, shotspeed = 5, bombpower = 500, bombspeed = -1.5, boostnrg = 8, }) Ships.addShip({ name = "Moth", desc = "Inertia bomber", sprite = 258, shotpower = 500, shotspeed = -1, shotenergy = 500, shotcd = 30, bombpower = 1000, bombspeed = 1, bombenergy = 800, boostnrg = 4, maxbspeed = 1.5, tripleshot = true, }) Ships.addShip({ name = "Swordfish III", desc = "On loan from Spike", sprite = 260, boostnrg = 8, maxspeed = 2, maxbspeed = 3.5, turnspeed = 3, bombpower = 0, shotpower = 500, shotspeed = 4, shotenergy = 400, tripleshot = true, tripleangle = 10, }) Ships.addShip({ name = "Probe", desc = "Lay mines and hide in a corner", sprite = 262, shotspeed = 1, tripleshot = true, bombpower = 4000, bombenergy = 500, bombspeed = 0, bombinertia = false, bombttl = 30.0, }) Ships.addShip({ name = "Goblin", desc = "Crowd control", sprite = 264, healnrg = 10, bombpower = 0, shotpower = 600, shotspeed = 2, shotenergy = 500, shotcd = 30, boostnrg = 0, maxspeed = 1, maxbspeed = 1, }) Ships.addShip({ name = "Bloodwing", desc = "Short range assault vehicle", sprite = 266, shotttl = 0.5, shotenergy = 300, bombttl = 0.5, bombenergy = 500, maxbspeed = 1.4, boostnrg = 6, }) Ships.addShip({ name = "Anubis", desc = "If you see this coming, run.", sprite = 268, shotttl = 0.5, shotenergy = 300, shotcd = 5, bombttl = 1.5, bombpower = 500, bombenergy = 100, bombspeed = 5, maxbspeed = 2, boostnrg = 6, }) Names = { "the_reaper","cooked_goose","pikachu","demon_lord","bad_things", "goose","iceman","maverick","picard","bad_headache","diarrhea", "doot_doot_doot","bullets","shiplord","rabid_capybara","bad_luck", "constipation","militant_vegan","vader","lol_gg","club_penguin", "spaaaaaace","dogzilla","shrapnel","a_moose_fart","boba_fett", "elon_musk","rage","boooooop","oops_sorry","gradius", "r-type","asdfpoo","nihilism","space_badger","not_an_alien", "sus_amogus","a_pixel","bad_steering","whatever","a_name" } Transform = class('Transform',Component) function Transform:onInit(x,y,rot,scale) self.x = x or 0 self.y = y or 0 self.rot = rot or 0 self.scale = scale or 1 self.lx = self.x self.lx = self.y end function Transform:onUpdate() -- Maybe do something with parent.child transforms? if self.lx ~= self.x or self.ly ~= self.y then self.entity.scene:registerBF(self) end if self.rot > 360 then self.rot = self.rot - 360.0 end if self.rot < 0 then self.rot = self.rot + 360.0 end end function Transform:onDeath() self.entity.scene:unregisterBF(self) end Velocity = class('Velocity',Component) function Velocity:onInit() self.x = 0 self.y = 0 self.ax = 0 self.ay = 0 self.rot = 0 end function Velocity:setEuler(angle,magnitude) self.x,self.y = math.cos(math.rad(angle)) * magnitude, math.sin(-math.rad(angle)) * magnitude end function Velocity:addEuler(angle,magnitude) local ax,ay = math.cos(math.rad(angle)) * magnitude, math.sin(math.rad(angle)) * magnitude self.x = self.x + ax self.y = self.y + ay end function Velocity:eulerAngle() return math.deg(math.atan(self.y,self.x)) end function Velocity:magnitude() return magnitude(self.x,self.y) end function Velocity:fmagnitude() return fmagnitude(self.x,self.y) end function Velocity:normalize() local mag = self:magnitude() self.x = self.x / mag self.y = self.y / mag end function Velocity:limit(max) local mag = self:magnitude() if mag > max then self.x = (self.x / mag) * max self.y = (self.y / mag) * max end end Mover = class('Mover',Component) function Mover:onInit() self.limit = 9999 end function Mover:onAdded() self.xf = self.entity:getComp(Transform) self.vel = self.entity:getComp(Velocity) local cam = self.entity.scene:getEnt('camera') self.camx = cam:getComp(Transform) self.rc = self.entity:getComp(RectCollider) self.sc = self.entity:getComp(ShipCtrl) end function Mover:onUpdate() if self.sc then self.vel:limit(self.sc:getMaxSpeed()) end if self.xf and self.vel then self.xf.x = self.xf.x + self.vel.x self.xf.x = self.xf.x + self.vel.ax self.xf.rot = self.xf.rot + self.vel.rot self.vel.ax = self.vel.ax * 0.99 if self.rc then self.rc:resolveX() end self.xf.y = self.xf.y + self.vel.y self.xf.y = self.xf.y + self.vel.ay self.vel.ay = self.vel.ay * 0.99 if self.rc then self.rc:resolveY() end --self.camx.scale = 1 end end TriRend = class('TriRend',Component) function TriRend:onInit(spr,w,h,debug) self.sprite = spr or 0 self.w = w or 1 self.h = h or 1 self.debug = debug or false self.x1,self.y1 = 0,0 self.x2,self.y2 = 0,0 self.x3,self.y3 = 0,0 self.x4,self.y4 = 0,0 -- Corner points for rot calc self.c1 = math.pi / 4 --math.rad(45) self.c2 = self.c1 * 3 --math.rad(135) self.c3 = self.c1 * 5 --math.rad(225) self.c4 = self.c1 * 7 --math.rad(314) self.rad = magnitude(self.w * 4,self.h * 4) self.cycle = true self:updateUV() end function TriRend:onAdded() self.xf = self.entity:getComp(Transform) local cam = self.entity.scene:getEnt('camera') if cam then self.cam = cam:getComp(Camera) self.camx = cam:getComp(Transform) end end function TriRend:onUpdate() if not self.xf then return end local aoff = math.rad(self.xf.rot) local scale = self.xf.scale or 1 local rs = self.rad * scale --trace(self.c1.. " " .. aoff .." x " .. rs) self.x1,self.y1 = math.cos(self.c1 + aoff) * rs, math.sin(self.c1 + aoff) * rs self.x2,self.y2 = math.cos(self.c2 + aoff) * rs, math.sin(self.c2 + aoff) * rs self.x3,self.y3 = math.cos(self.c3 + aoff) * rs, math.sin(self.c3 + aoff) * rs self.x4,self.y4 = math.cos(self.c4 + aoff) * rs, math.sin(self.c4 + aoff) * rs end function TriRend:updateUV() local sry = ((self.sprite) // 16) * 8 local srx = (self.sprite - sry) * 8 local erx, ery = srx + (self.w * 8) - 1, sry + (self.h * 8) - 1 self.u1,self.v1 = srx, sry --1 2 self.u2,self.v2 = srx, ery --4 3 self.u3,self.v3 = erx, ery self.u4,self.v4 = erx, sry end function TriRend:onDraw() if not self.xf then return end local scale = 1 --self.xf.scale or 1 local x,y = self.xf.x ,self.xf.y --self.cam:toSS(self.xf.x,self.xf.y) local x1,y1 = self.cam:toSS(self.x1 + x,self.y1 + y) local x2,y2 = self.cam:toSS(self.x2 + x,self.y2 + y) local x3,y3 = self.cam:toSS(self.x3 + x,self.y3 + y) local x4,y4 = self.cam:toSS(self.x4 + x,self.y4 + y) --trace(self.x1..","..self.y1.."-"..self.x2..","..self.y2) --trace(self.x3..","..self.y3.."-"..self.x4..","..self.y4) if self.scale and self.scale ~= 1 then x1,y1,x2,y2,x3,y3,x4,y4 = x1*self.scale,y1*self.scale,x2*self.scale,y2*self.scale, x3*self.scale,y3*self.scale,x4*self.scale,y4*self.scale end textri(x1,y1,x2,y2,x3,y3, self.u1,self.v1,self.u2,self.v2,self.u3,self.v3, false,0) textri(x1,y1,x3,y3,x4,y4, self.u1,self.v1,self.u3,self.v3,self.u4,self.v4, false,0) if self.debug then local sx,sy = self.cam:toSS(x,y) circb(sx,sy,8 * self.xf.scale,5) pix(x2,y2,6) pix(x3,y3,6) pix(x4,y4,6) line(x1,y1,x3,y3,6) line(x2,y2,x4,y4,6) pix(x1,y1,11) print(fmtVec(x1,y1),math.min(x1,x2,x3,x4),math.min(y1,y2,y3,y4) - x1 // 5 - 5,11,true,1,true) end --print(fmtVec(x,y),x1,y1-10 - x1,5) --print(fmtVec(self.xf.x,self.xf.y),x1,y1-20,5) end MapRend = class('MapRend',Component) function MapRend:onInit() self.base = 0x08000 self.cols = 240 self.sx = 0 self.sy = 0 self.w = 240 self.h = 136 end function MapRend:onUpdate() -- late binding if not self.cam then local cent = self.entity.scene:getEnt("camera") self.cam = cent:getComp(Camera) self.camx = cent:getComp(Transform) end --if self.camx then -- self.xf.scale = self.camx.scale --end end function MapRend:onAdded() self.xf = self.entity:getComp(Transform) end function MapRend:onDraw() local msx = math.max(0,self.cam.x1//8 - 1) local msy = math.max(0,self.cam.y1//8 - 1) local mex = math.min(240,self.cam.x2//8 + 1) local mey = math.min(136,self.cam.y2//8 + 1) --print('MAP: '..fmtVec(msx,msy)..'->'..fmtVec(mex,mey).."SCALE:"..self.xf.scale,10,30,5) for y = msy,mey do for x = msx,mex do local spr = mget(x,y) if fget(spr,7) then if spr == nil then trace("NILSPR1:"..x..","..y) end local lspr = spr spr = AniTiles.get(spr) if spr == nil then trace("NILSPR2:"..lspr.." "..x..","..y) end end local srx = (spr - (spr // 16 * 16)) * 8 local sry = (spr // 16) * 8 local tx, ty = (x * 8),(y * 8) tx,ty = self.cam:toSS(tx,ty) orthQuad(tx,ty,tx+(8 * self.camx.scale),ty+(8 * self.camx.scale),srx,sry,srx+8,sry+8,false,0) end end end Camera = class('Camera',Component) function Camera:onInit() self.offx = 0 self.offy = 0 end function Camera:onAdded() self.xf = self.entity:getComp(Transform) self.target = nil self.sw = 240 self.sh = 136 self.x1 = 0 self.y1 = 0 self.x2 = 239 self.y2 = 135 end function Camera:follow(xform) if xform ~= nil and xform:isInstanceOf(Transform) then self.target = xform self.vel = self.target.entity:getComp(Velocity) else self.target = nil end end function Camera:onUpdate() if key(27) and Prefs.debug then self.xf.scale = 1 end if key(38) and Prefs.debug then self.xf.scale = self.xf.scale + 0.01 end if key(37) and Prefs.debug then self.xf.scale = self.xf.scale - 0.01 end if self.target then self.xf.x = self.target.x self.xf.y = self.target.y --self.offx = lerp(self.offx,20 * self.vel.x,0.03) --self.offy = lerp(self.offy,20 * self.vel.y,0.03) end self.x1 = self.xf.x - (self.sw / 2 / self.xf.scale) + self.offx self.y1 = self.xf.y - (self.sh / 2 / self.xf.scale) + self.offy self.x2 = self.xf.x + (self.sw / 2 / self.xf.scale) + self.offx self.y2 = self.xf.y + (self.sh / 2 / self.xf.scale) + self.offy --print(self.xf.scale,10,50,5) end function Camera:toSS(x,y) local scale = self.xf.scale local hw,hh = self.sw / 2, self.sh / 2 local cx, cy = self.xf.x, self.xf.y --self.offx = self.xf.x / 2 --self.offy = self.xf.y / 2 local rx,ry = ((x * scale) + hw - (self.xf.x * scale)), ((y * scale) + hh - (self.xf.y * scale)) if scale == 1 then rx,ry = rx//1, ry//1 end return rx,ry end function Camera:print(text,x,y,color,fixed,scale,smallfont) x,y = self:toSS(x,y) print(text,x,y,color,fixed,scale,smallfont) end function Camera:line(x0, y0, x1, y1, color) x0,y0 = self:toSS(x0,y0) x1,y1 = self:toSS(x1,y1) line(x0,y0,x1,y1,color) end function Camera:rect(x, y, w, h, color) local nx,ny = self:toSS(x,y) local x1,y1 = self:toSS(x+w,y+h) local nw,nh = x1-nx,y1-ny rect(nx,ny,nw,nh,color) end function Camera:rectb(x, y, w, h, color) local nx,ny = self:toSS(x,y) local x1,y1 = self:toSS(x+w,y+h) local nw,nh = x1-nx,y1-ny rectb(nx,ny,nw,nh,color) end function Camera:circ(x,y,radius,color) local nx,ny = self:toSS(x,y) local nrad = radius * self.xf.scale circ(nx,ny,nrad,color) end function Camera:circb(x,y,radius,color) local nx,ny = self:toSS(x,y) local nrad = radius * self.xf.scale circb(nx,ny,nrad,color) end function Camera:pix(x,y,color) x,y = self:toSS(x,y) pix(x,y,color) end function Camera:spr(id,x,y,colorkey,scale,flip,rotate,w,h) x,y = self:toSS(x,y) colorkey = colorkey or -1 scale = scale or 1 flip = flip or 0 rotate = rotate or 0 w = w or 1 h = h or 1 spr(id,x,y,colorkey,scale,flip,rotate,w,h) end Bullet = class('Bullet',Component) function Bullet:onInit() self.max_bounces = 2 self.bounces = 0 self.lifetime = 5 self.power = 1000 self.owner = nil end function Bullet:onAdded() self.xf = self.entity:getComp(Transform) self.vel = self.entity:getComp(Velocity) self.rc = self.entity:getComp(RectCollider) local cent = self.entity.scene:getEnt("camera") self.cam = cent:getComp(Camera) self.camx = cent:getComp(Transform) end function Bullet:onUpdate() self.lifetime = self.lifetime - Timer.delta if self.lifetime <= 0 then self.entity:destroy() return end local ox,oy = self.xf.x, self.xf.y self.xf.x = self.xf.x + self.vel.x if self.rc:resolveX(ox) then self.bounces = self.bounces + 1 end self.xf.y = self.xf.y + self.vel.y if self.rc:resolveY() then self.bounces = self.bounces + 1 end if self.bounces > self.max_bounces then self.entity:destroy() return end local xfs = self.entity.scene:xfInRadius(self.xf.x,self.xf.y,30) for i = 1,#xfs do local rc = xfs[i].entity:getComp(RectCollider) if rc ~= nil and rc.group ~= 'projectile' and rc.group ~= 'pickup' then if rectOverlap(self.xf.x + self.rc.x1,self.xf.y + self.rc.y1, self.xf.x + self.rc.x2,self.xf.y + self.rc.y2, xfs[i].x + rc.x1,xfs[i].y + rc.y1, xfs[i].x + rc.x2,xfs[i].y + rc.y2) then if rc.entity ~= self.owner and self.entity ~= xfs[i].entity then local sc = xfs[i].entity:getComp(ShipCtrl) if sc ~= nil then sc:damage(self.power,self.owner) end self.entity:destroy() return end end end end end function Bullet:onDraw() local cx,cy = self.cam:toSS(self.xf.x,self.xf.y) pix(cx,cy,5) end Bomb = class('Bomb',Component) function Bomb:onInit() self.max_bounces = 2 self.bounces = 0 self.shrap_count = 8 self.emp = false self.lifetime = 5 self.power = 2000 self.owner = nil self.para = false end function Bomb:onAdded() self.xf = self.entity:getComp(Transform) self.vel = self.entity:getComp(Velocity) self.rc = self.entity:getComp(RectCollider) local cent = self.entity.scene:getEnt("camera") self.cam = cent:getComp(Camera) self.camx = cent:getComp(Transform) self.flip = 0 end function Bomb:onUpdate() self.lifetime = self.lifetime - Timer.delta if self.lifetime <= 0 then self.entity:destroy() return end self.xf.x = self.xf.x + self.vel.x self.xf.x = self.xf.x + self.vel.ax self.vel.ax = self.vel.ax * 0.99 if self.rc:resolveX() then self.bounces = self.bounces + 1 end self.xf.y = self.xf.y + self.vel.y self.xf.y = self.xf.y + self.vel.ay self.vel.ay = self.vel.ay * 0.99 if self.rc:resolveY() then self.bounces = self.bounces + 1 end if self.bounces > self.max_bounces then self:explode() return end local xfs = self.entity.scene:xfInRadius(self.xf.x,self.xf.y,30) for i = 1,#xfs do local rc = xfs[i].entity:getComp(RectCollider) if rc ~= nil and rc.group ~= 'projectile' and rc.group ~= 'pickup' then if rectOverlap(self.xf.x + self.rc.x1,self.xf.y + self.rc.y1, self.xf.x + self.rc.x2,self.xf.y + self.rc.y2, xfs[i].x + rc.x1,xfs[i].y + rc.y1, xfs[i].x + rc.x2,xfs[i].y + rc.y2) then if rc.entity ~= self.owner and self.entity ~= xfs[i].entity then local sc = xfs[i].entity:getComp(ShipCtrl) if sc ~= nil then sc:damage(self.power,self.owner) end self:explode() return end end end end self.flip = self.flip + 1 if self.flip >= 4 then self.flip = 0 end end function Bomb:onDraw() local cx1,cy1 = self.cam:toSS((self.xf.x-4),(self.xf.y-4)) local cx2,cy2 = self.cam:toSS((self.xf.x+4),(self.xf.y+4)) local spr = 400 if self.para then spr = 401 end spriteQuad(spr,cx1,cy1,cx2,cy2,0,self.flip) end function Bomb:explode() if self.shrap_count > 0 then for i = 0,359.999,(360/self.shrap_count) do fireBullet(self.entity.scene,self.owner, self.xf.x, self.xf.y, math.cos(math.rad(i + 90)) * 3, math.sin(math.rad(i + 90)) * 3, 400) end end self.entity:destroy() end Shockwave = class('Shockwave',Component) function Shockwave:onInit(radius,force,owner) self.force = force or 2 self.forcerad = radius or 40 self.radius = 0 self.owner = owner end function Shockwave:onAdded() self.xf = self.entity:getComp(Transform) local cent = self.entity.scene:getEnt("camera") self.cam = cent:getComp(Camera) end function Shockwave:onUpdate() self.radius = self.radius + 2 if self.radius >= self.forcerad then self.entity:destroy() end if self.entity then local xlist = self.entity.scene:xfInRadius(self.xf.x,self.xf.y,self.forcerad) for k,xf in ipairs(xlist) do e = xf.entity if e.name ~= self.entity.name and e.name ~= 'camera' and e ~= self.owner then vel = xf.entity:getComp(Velocity) if vel then local ato = math.deg(angleBetween(self.xf.x,self.xf.y,xf.x,xf.y)) vel:setEuler(ato,self.force) b = e:getComp(Bullet) if b then b.owner = self.owner else b = e:getComp(Bomb) if b then b.owner = self.owner end end end end end end end function Shockwave:onDraw() lc = 0 for rd = self.radius,0,-3 do if lc < 8 then self.cam:circb(self.xf.x,self.xf.y,rd,8 + lc) end lc = lc + 1 end end Input = class('Input',Component) function Input:onAdded() self.xf = self.entity:getComp(Transform) self.vel = self.entity:getComp(Velocity) self.mv = self.entity:getComp(Mover) self.sc = self.entity:getComp(ShipCtrl) end function Input:onUpdate() local ms = self.sc:getMaxSpeed() if btn(0) then self.sc:thrust() end if btn(1) then self.sc:reverse() end if btn(2) then self.sc:turnLeft() end if btn(3) then self.sc:turnRight() end if btn(4) then if self.sc.safe then self.vel:setEuler(0,0) else self.sc:fireBullet() end end if btn(5) and self.sc.boostnrg ~= 0 then self.sc.boost = true else self.sc.boost = false end if btn(6) then self.sc:layMine() end if btn(7) then self.sc:fireShockwave() end if keyp(12) and Prefs.debug then self.sc:die('explode') end -- P - Para toggle if keyp(16) and Prefs.debug then self.sc.para = not self.sc.para end -- C - Camera attach if keyp(3) and Prefs.debug then local came = self.entity.scene:getEnt('camera') local cam = came:getComp(Camera) if cam.target == nil then cam:follow(self.xf) else cam:follow(nil) end end -- T - Teleport if keyp(20) and Prefs.debug then self.sc:teleport(10*8,10*8) end end RectCollider = class('RectCollider',Component) function RectCollider:onInit(x,y,w,h,grp) self.x1 = x self.y1 = y self.x2 = x + w self.y2 = y + h self.bounce_factor = 0.75 self.debug = false self.group = grp or '' end function RectCollider:onAdded() self.xf = self.entity:getComp(Transform) self.vel = self.entity:getComp(Velocity) self.cam = self.entity.scene.cam --self.camx = self.entity.scene.came:getComp(Transform) end function RectCollider:onUpdate() end function RectCollider:onDraw() if self.debug then local x,y = self.xf.x,self.xf.y if Prefs.debug then self.cam:rectb( x+self.x1, y+self.y1, self.x2-self.x1, self.y2-self.y1, 5 ) end end end function RectCollider:resolveX() if self.xf then local tx,ty = self.x1 + self.xf.x, self.y1 + self.xf.y + 0.49 local bx,by = self.x2 + self.xf.x, self.y2 + self.xf.y - 0.49 local col = false local dx = self.xf.x -- Map collision test if self.vel.x > 0 then if solid(bx,ty) or solid(bx,ty+((by-ty)//2)) or solid(bx,by) then dx = dx - (bx - ((bx // 8) * 8)) self.vel.x = self.vel.x * -self.bounce_factor self.vel.y = self.vel.y * self.bounce_factor col = true end elseif self.vel.x < 0 then if solid(tx,ty) or solid(tx,ty+((by-ty)//2)) or solid(tx,by) then dx = dx - (bx - (bx // 8 * 8)) + 8 self.vel.x = self.vel.x * -self.bounce_factor self.vel.y = self.vel.y * self.bounce_factor col = true end end self.xf.x = dx return col end end function RectCollider:resolveY() if self.xf then local tx,ty = self.x1 + self.xf.x + 0.49, self.y1 + self.xf.y local bx,by = self.x2 + self.xf.x - 0.49, self.y2 + self.xf.y local col = false local dx,dy = self.xf.x,self.xf.y -- Map collision test if self.vel.y > 0 then if solid(tx,by) or solid(tx+((bx-tx)//2),by) or solid(bx,by) then dy = dy - (by - ((by // 8) * 8)) self.vel.y = self.vel.y * -self.bounce_factor self.vel.x = self.vel.x * self.bounce_factor col = true end elseif self.vel.y < 0 then if solid(tx,ty) or solid(tx+((bx-tx)//2),ty) or solid(bx,ty) then dy = dy + (((ty // 8) * 8) - ty) + 8 self.vel.y = self.vel.y * -self.bounce_factor self.vel.x = self.vel.x * self.bounce_factor col = true end end self.xf.y = dy return col end end BlackHole = class('BlackHole',Component) function BlackHole:onInit() self.radius = 100 self.timer = 0 end function BlackHole:onAdded() self.xf = self.entity:getComp(Transform) local cent = self.entity.scene:getEnt("camera") self.cam = cent:getComp(Camera) self.camx = cent:getComp(Transform) end function BlackHole:onUpdate() if not self.xf then return end self.timer = self.timer + 1 if self.timer > 40 then self.timer = 0 end local xlist = self.entity.scene:xfInRadius(self.xf.x,self.xf.y,self.radius) for k,v in ipairs(xlist) do if v.entity.name ~= self.entity.name and v.entity.name ~= 'camera' then vel = v.entity:getComp(Velocity) xf = v.entity:getComp(Transform) if vel then local d = dist(self.xf.x,self.xf.y,v.x,v.y) if d < 4 then -- If this is a ship, zap all the energy and teleport -- otherwise die sc = v.entity:getComp(ShipCtrl) if sc then sc:teleport(math.random(10,80) * 8,math.random(10,80) * 8) vel.ax = 0 vel.ay = 0 sc:damage((sc.energy // 4) * 3) sc:paralyze(100) else v.entity:destroy() end elseif d <= self.radius then local vx,vy = self.xf.x - v.x, self.xf.y - v.y local normx,normy = normalize(vx,vy) local m = 5000 local G = 10 local delta = 0.12 local a = (m * G / (math.max(d,20) ^ 2)) * 0.12 vel.ax = vel.ax + (normx * a) / 50 vel.ay = vel.ay + (normy * a) / 50 --trace(fmtVec(vel.ax,vel.ay)) self.cam:line(xf.x,xf.y,xf.x + (vel.ax * 2),xf.y + (vel.ay * 2),11) local p1x,p1y = self.cam:toSS(self.xf.x,self.xf.y) local p2x,p2y = self.cam:toSS(v.x,v.y) --print(fmtVec(vx/,vy/100),p1x,p1y - 10,5) if Prefs.debug then line(p1x,p1y,p2x,p2y,5) end end end end end end function BlackHole:onDraw() local cx1,cy1 = self.cam:toSS((self.xf.x-8),(self.xf.y-8)) local cx2,cy2 = self.cam:toSS((self.xf.x+8),(self.xf.y+8)) spriteQuad(288 + (2 * (self.timer // 10)),cx1,cy1,cx2,cy2,0,self.flip,2,2) end SeekState = class('SeekState',State) function SeekState:enter() self.cam = self.ctx.scene:getEnt('camera'):getComp(Camera) self.xf = self.ctx:getComp(Transform) self.vel = self.ctx:getComp(Velocity) self.mv = self.ctx:getComp(Mover) self.sc = self.ctx:getComp(ShipCtrl) if self.sc.target == nil then self.sc:targetNearest(self.xf.x,self.xf.y,200) if self.sc.target then self.pxf = self.target:getComp(Transform) else self.fsm:transition("patrol") end end self.avoid = 0 end function SeekState:exit() end function SeekState:update() --self.mv.limit = .2 if self.sc.target == nil then self.fsm:transition("patrol") end local pa,pd = 0,100000 if self.sc.target then pd = dist(self.xf.x,self.xf.y,self.sc.target.x,self.sc.target.y) local dx = self.sc.target.x - self.xf.x; local dy = self.sc.target.y - self.xf.y; normalize(dx,dy); local lr = math.atan(-dy, -dx) pa = eulerBetween(self.xf.rot,math.deg(lr) + 180) if Prefs.debug then self.cam:print("Seek",self.xf.x,self.xf.y - 10,11,false,1,true) self.cam:line(self.xf.x,self.xf.y,self.sc.target.x,self.sc.target.y,11) end end if self.xf then local rad = math.rad(self.xf.rot) local tx,ty = self.xf.x + math.cos(rad) * 15,self.xf.y + math.sin(rad) * 15 self.cam:line(self.xf.x,self.xf.y,tx,ty,15) if fget(mget(tx//8,ty//8),0) then self.avoid = (math.random(1) * 2 - 1) * 20 end end if pd > 100 then self.sc.target = nil self.fsm:transition("patrol") end if pa > 2 or self.avoid > 0 then self.sc:turnRight() elseif pa < -2 or self.avoid < 0 then self.sc:turnLeft() end if self.avoid > 0 then self.avoid = self.avoid - 1 self.sc:reverse() else self.sc:thrust() end if math.abs(pa) < 2 and self.sc.energy > 700 then if self.sc.energy > 700 and pa < 30 and self.sc.bombpower > 0 and self.sc.bombspeed > 0 then self.sc:fireBomb() else self.sc:fireBullet() end end if self.sc.energy < 1500 then self.fsm:transition("evade") end end PatrolState = class('PatrolState',State) function PatrolState:enter() self.cam = self.ctx.scene:getEnt('camera'):getComp(Camera) self.xf = self.ctx:getComp(Transform) self.vel = self.ctx:getComp(Velocity) self.mv = self.ctx:getComp(Mover) self.sc = self.ctx:getComp(ShipCtrl) self.dtime = 0 self.dturn = 0 self.backup = false --self.player = self.ctx.scene:getEnt('player') --if self.player then -- self.pxf = self.player:getComp(Transform) --end end function PatrolState:exit() end function PatrolState:update() self.mv.limit = .2 local pd = 100000 if self.sc.target == nil and self.dtime <= 1 then self.sc:targetNearest(self.xf.x,self.xf.y,100) if self.sc.target then self.fsm:transition("seek") end end if Prefs.debug then self.cam:print("Patrol",self.xf.x,self.xf.y - 10,11,false,1,true) end if self.xf then local rad = math.rad(self.xf.rot) local tx,ty = self.xf.x + math.cos(rad) * 15,self.xf.y + math.sin(rad) * 15 self.cam:line(self.xf.x,self.xf.y,tx,ty,15) if fget(mget(tx//8,ty//8),0) then self.backup = true self.dturn = -2 self.dtime = 30 end end self.dtime = self.dtime - 1 if self.dtime <= 0 then self.backup = false self.dtime = math.random(20,200) self.dturn = math.random (-1,1) * 2 if self.dturn ~= 0 then self.dtime = self.dtime // 4 end end if self.dturn > 0 then self.sc:turnRight() elseif self.dturn < 0 then self.sc:turnLeft() end if self.backup then self.sc:reverse() else self.sc:thrust() end end EvadeState = class('EvadeState',State) function EvadeState:enter() self.cam = self.ctx.scene:getEnt('camera'):getComp(Camera) self.xf = self.ctx:getComp(Transform) self.vel = self.ctx:getComp(Velocity) self.mv = self.ctx:getComp(Mover) self.sc = self.ctx:getComp(ShipCtrl) self.dtime = 0 self.dturn = 0 if self.sc.target == nil then self.sc:targetNearest(self.xf.x,self.xf.y,100) --if self.sc.target then -- self.pxf = self.target:getComp(Transform) --end --else --self.pxf = self.sc.target end end function EvadeState:exit() end function EvadeState:update() self.mv.limit = .2 if Prefs.debug then self.cam:print("Evade",self.xf.x,self.xf.y - 10,11,false,1,true) end self.sc:targetNearest(self.xf.x,self.xf.y,100) local pd,pa = 100000,0 if self.sc.target ~= nil then pd = dist(self.xf.x,self.xf.y,self.sc.target.x,self.sc.target.y) local dx = self.sc.target.x - self.xf.x; local dy = self.sc.target.y - self.xf.y; normalize(dx,dy); local lr = math.atan(-dy, -dx) pa = eulerBetween(self.xf.rot,math.deg(lr) + 180) end self.dtime = self.dtime - 1 if self.dtime <= 0 then self.dtime = math.random(20,200) self.dturn = math.random (-1,1) * 2 if self.dturn ~= 0 then self.dtime = self.dtime // 4 end end if self.dturn ~= 0 then self.xf.rot = (self.xf.rot + self.dturn) % 360.0 --math.min(2,-pa) / 10 elseif pa > 2 then self.xf.rot = (self.xf.rot - 2) % 360.0 --math.max(-2,-pa) / 10 elseif pa < -2 then self.xf.rot = (self.xf.rot + 2) % 360.0 --math.min(2,-pa) / 10 end if self.sc.energy > 2000 then if pd > 100 then self.fsm:transition("patrol") else self.fsm:transition("seek") end elseif self.sc.energy > 100 and pd < 60 then self.sc.boost = true self.sc.energy = self.sc.energy - 3 self.vel:addEuler(self.xf.rot,0.1) else self.sc.boost = false self.vel:addEuler(self.xf.rot,0.05) end end AI = class('AI',Component) function AI:onInit() end function AI:onAdded() self.fsm = FSM:new(self.entity) self.seekState = SeekState:new() self.patrolState = PatrolState:new() self.evadeState = EvadeState:new() self.fsm:add(self.evadeState,"evade") self.fsm:add(self.patrolState,"patrol") self.fsm:add(self.seekState,"seek") self.fsm:transition("patrol") end function AI:onUpdate() self.fsm:update() end Exhaust = class('Exhaust',Component) function Exhaust:onInit() self.ps = {} end function Exhaust:onAdded(settings) self.xf = self.entity:getComp(Transform) self.sc = self.entity:getComp(ShipCtrl) self.tr = self.entity:getComp(TriRend) local cent = self.entity.scene:getEnt("camera") local cam = cent:getComp(Camera) self.ps = PSystem({ maxPart = 50, lifetime = 20, rate = 0, emitCt = 0, x = {0,0}, y = {0,0}, vx = {0,0}, vy = {0,0}, emitfn = function (p) local sett = p[11] local f = sett.frames cam:spr(f[(p[7] / p[8] * #f) // 1 + 1],p[1],p[2],0) end, emitSett = {frames={480,481,482,483,484}}, affectors = {} }) end function Exhaust:onUpdate() self.ps.x = self.xf.x + ((self.tr.x3 - self.tr.x4) / 2) - 4 self.ps.y = self.xf.y + ((self.tr.y3 - self.tr.y4) / 2) - 4 self.ps:update() if self.sc.boost then self.ps:spawn(1) end end function Exhaust:onDraw() self.ps:draw() end Explosion = class('Explosion',Component) function Explosion:onInit() self.ps = {} end function Explosion:onAdded(settings) self.xf = self.entity:getComp(Transform) local cent = self.entity.scene:getEnt("camera") local cam = cent:getComp(Camera) self.ps = PSystem({ maxPart = 150, lifetime = 40, rate = 0, emitCt = 0, --x = {0,0}, y = {0,0}, r = {2,5}, angle = {0,2 * math.pi}, vx = 0, vy = 0, emitfn = function (p) local sett = p[11] local f = sett.frames cam:spr(f[(p[7] / p[8] * #f) // 1 + 1],p[1],p[2],0) end, --emitSett = {frames={480,481,482,483,484}}, emitSett = {frames={ 464,465,466,467,468,469, 502,503,504,505,506,507}}, affectors = { {AffectFn.repulse,{ strength = 0.05, radius = 5, x = self.xf.x, y = self.xf.y }} } }) self.ps.x,self.ps.y = self.xf.x,self.xf.y self.ps:spawn(150) end function Explosion:onUpdate() self.ps:update() if self.ps.count == 0 then self.entity:destroy() end end function Explosion:onDraw() self.ps:draw() end ShipCtrl = class('ShipCtrl',Component) function ShipCtrl:onInit(stype) stype = stype or 1 for k,v in pairs(Ships.defaults) do self[k] = v end for k,v in pairs(Ships[stype]) do self[k] = v end --[[ self.maxenergy = 2600 self.energy = 2600 self.alive = true self.para = false self.boost = false self.accel = 0.05 self.maxspeed = 1 self.turnspeed = 2 self.baccel = 0.1 self.maxbspeed = 2 self.healnrg = 5 self.boostnrg = 3 self.shotcd = 40 self.shottimer = 0 self.shaketimer = 0 self.shakeamt = 0 self.targrange = 100 ]] self.energy = self.maxenergy -- maybe add stuff for special states, cloaked, etc self.target = nil end function ShipCtrl:onAdded() self.xf = self.entity:getComp(Transform) self.vel = self.entity:getComp(Velocity) self.tr = self.entity:getComp(TriRend) self.ecam = self.entity.scene:getEnt('camera') self.cam = self.ecam:getComp(Camera) end function ShipCtrl:onUpdate() -- TODO: Make this faster, check corners, then sides, then center local tltx,tlty = self.xf.x-8,self.xf.y-8 local brtx,brty = self.xf.x+8,self.xf.y+8 local wsafe = self.safe self.safe = false for scy = tlty,brty,8 do for scx = tltx,brtx,8 do if Prefs.debug then self.cam:pix(scx,scy,4) end if safe(scx,scy) then self.safe = true self.energy = self.maxenergy scx = 999999 scy = 999999 if not wsafe then local scn = self.entity.scene local rm = 0 for ix = 1,#scn.ents do local ent = scn.ents[ix] cmp = ent:getComp(Bullet) if cmp == nil then cmp = ent:getComp(Bomb) end if cmp then if cmp.owner == self.entity then if self.entity.name == "player" then rm = rm + 1 end ent:destroy() end end end if self.entity.name == "player" then trace("RM:" .. rm) end end -- remove all bullets, boms if was not safe but now safe end end end if self.energy < 0 then self:die(nil) end if self.boost then self.energy = math.max(1,self.energy - self.boostnrg) elseif self.energy < self.maxenergy and not self.para then self.energy = math.min(self.maxenergy,self.energy + self.healnrg) end if self.shaketimer > 0 then poke(0x3FF9,math.random(-self.shakeamt,self.shakeamt)) poke(0x3FF9+1,math.random(-self.shakeamt,self.shakeamt)) self.shaketimer = self.shaketimer - 1 if self.shaketimer == 0 then memset(0x3FF9,0,2) end end if self.shottimer > 0 then self.shottimer = self.shottimer - 1 end if self.paratimer > 0 then self.para = true self.paratimer = self.paratimer - 1 if self.paratimer == 0 then self.para = false end end if self.shocktimer > 0 then self.shocktimer = self.shocktimer - 1 end if self.target ~= nil then -- Make sure it is still alive if self.target.entity == nil then self.target = nil else -- If outside range, unlink target local ndist = dist(self.xf.x,self.xf.y,self.target.x,self.target.y) if ndist > self.targrange then self.target = nil else esc = self.target.entity:getComp(ShipCtrl) if esc then if esc.safe then self.target = nil end end end end end end function ShipCtrl:getAccel() if self.boost then return self.baccel end return self.accel end function ShipCtrl:getMaxSpeed() if self.boost then return self.maxbspeed end return self.maxspeed end function ShipCtrl:die(type) local pl = self.entity.scene.arena:getPilot(self.entity.name) if pl then pl.entity = nil end -- Handle death effect here -- maybe things like nothing, explode, frag, etc if type == "explode" then local cam = self.entity.scene.cam local e = self.entity.scene:addEnt("explosion") local xf = e:addComp(Transform) xf.x,xf.y = self.xf.x - 4, self.xf.y - 4 e:addComp(Explosion) local as = self.entity.scene:addEnt("corpse") local at = as:addComp(Transform,self.xf.x,self.xf.y) local av = as:addComp(Velocity) av.rot = math.random(-10,10) av:setEuler(math.random(360),math.random()) local aspr = math.random(416,419) as:addComp(TriRend,aspr,1,1) as:addComp(DeathTimer,5) as:addComp(Mover) as:addComp(Bleeding) else trace("NO DIE") end if self.entity.scene.player == self.entity then Prefs.deaths = Prefs.deaths + 1 memset(0x3FF9,0,2) Timer:onTime(5.0,self.entity.scene,function(ctx) local e = ctx.arena:spawnPilot(ctx.arena.player_pilot.id) local xf = e:getComp(Transform) local sc = e:getComp(ShipCtrl) sc:teleport(100,100) ctx.cam:follow(xf) end) end -- If some stuff... local nc = Factory.createContainer(self.entity.scene) local xf = nc:getComp(Transform) xf.x, xf.y = self.xf.x, self.xf.y local vel = nc:getComp(Velocity) vel.x,vel.y,vel.rot = self.vel.x/3,self.vel.y/3,2 self.entity:destroy() end function ShipCtrl:fireBullet() -- returns true if fired if self.safe then return end local function doshot(angle,speed,power) return fireBullet(self.entity.scene,self.entity, self.xf.x + ((self.tr.x1 - self.tr.x2) / 2), self.xf.y + ((self.tr.y1 - self.tr.y2) / 2), self.vel.x + (math.cos(angle) * speed), self.vel.y + (math.sin(angle) * speed), power) end if self.energy > self.shotenergy and self.shottimer == 0 then local s1,s2,s3 = nil,nil,nil s1 = doshot(math.rad(self.xf.rot),self.shotspeed,self.shotpower) s1.lifetime = self.shotttl if self.tripleshot then s2 = doshot(math.rad(self.xf.rot - self.tripleangle),self.shotspeed,self.shotpower) s3 = doshot(math.rad(self.xf.rot + self.tripleangle),self.shotspeed,self.shotpower) s2.lifetime = self.shotttl s3.lifetime = self.shotttl end self.energy = self.energy - self.shotenergy self.shottimer = self.shotcd return true end return false end function ShipCtrl:fireBomb() -- returns true if fired if self.safe then return end if self.bombpower == 0 then return end if self.energy > self.bombenergy and self.shottimer == 0 then local vx,vy = 0,0 if self.bombinertia then vx,vy = self.vel.x + (math.cos(math.rad(self.xf.rot)) * self.bombspeed), self.vel.y + (math.sin(math.rad(self.xf.rot)) * self.bombspeed) end local bm = fireBomb(self.entity.scene,self.entity, self.xf.x + ((self.tr.x1 - self.tr.x2) / 2), self.xf.y + ((self.tr.y1 - self.tr.y2) / 2), vx, vy, self.bombpower ) bm.lifetime = self.bombttl self.energy = self.energy - self.bombenergy self.shottimer = self.shotcd return true end return false end -- TODO: Combine with bomb function ShipCtrl:layMine() if self.safe then return end if self.bombpower == 0 then return end if self.energy > self.bombenergy and self.shottimer == 0 then local vx,vy = 0,0 if self.bombinertia then vx,vy = self.vel.x + (math.cos(math.rad(self.xf.rot)) * self.bombspeed), self.vel.y + (math.sin(math.rad(self.xf.rot)) * self.bombspeed) end local bm = fireBomb(self.entity.scene,self.entity, self.xf.x + ((self.tr.x1 - self.tr.x2) / 2), self.xf.y + ((self.tr.y1 - self.tr.y2) / 2), vx, vy, self.bombpower ) bm.lifetime = self.bombttl bm.para = true self.energy = self.energy - self.bombenergy self.shottimer = self.shotcd return true end return false end function ShipCtrl:fireShockwa#lve() if self.safe then return end if self.shocktimer == 0 then self.shocktimer = self.shockcd sw = self.entity.scene:addEnt('shock_'..self.entity.name) xf = sw:addComp(Transform,self.xf.x,self.xf.y) swc = sw:addComp(Shockwave,nil,nil,self.entity) end end function ShipCtrl:teleport(x,y) self.xf.x = x self.xf.y = y self.entity:addComp(Teleport) -- TODO: some effects? end function ShipCtrl:damage(energy,by) if self.safe then return end if not Prefs.god or (Prefs.god and self.entity ~= self.entity.scene.player) then self.energy = self.energy - energy end local cam = self.entity.scene.cam if cam.target ~= nil and self.entity == cam.target.entity then self:shake(3,10) end if self.energy <= 0 then local s = self.entity.scene local arena = s.arena local name = "Unknown" if by.name then name = by.name --killer, self is killed local pl = arena:getPilot(name) if pl then if by.name ~= self.entity.name then pl.kills = pl.kills + 1 pl.score = pl.score + 5 end end end if name ~= "Unknown" then local pl = arena:getPilot(self.entity.name) if pl then pl.deaths = pl.deaths + 1 pl.score = pl.score - 5 end end arena:addLog(2.0,self.entity.name.." killed by "..name) if by == s.player then Prefs.kills = Prefs.kills + 1 end arena:updateRankings() self:die("explode") end end function ShipCtrl:shake(amt,len) self.shakeamt = amt self.shaketimer = len end function ShipCtrl:paralyze(len) self.paratimer = len end function ShipCtrl:getContainer() self.energy = self.maxenergy -- Get a random powerup, based on pool for arena end function ShipCtrl:thrust() self.vel:addEuler(self.xf.rot,self:getAccel()) end function ShipCtrl:reverse() self.vel:addEuler(self.xf.rot,-self:getAccel()) end function ShipCtrl:turnLeft() self.xf.rot = self.xf.rot - self.turnspeed end function ShipCtrl:turnRight() self.xf.rot = self.xf.rot + self.turnspeed end function ShipCtrl:targetNearest(x,y) local odist = self.targrange local xfs = self.entity.scene:xfInRadius(x,y,self.targrange) for i = 1,#xfs do if xfs[i] ~= nil and xfs[i].entity ~= nil and xfs[i].entity ~= self.entity then local sc = xfs[i].entity:getComp(ShipCtrl) if sc then local ndist = dist(self.xf.x,self.xf.y,xfs[i].x,xfs[i].y) if ndist < odist and not sc.safe then self.target = xfs[i] odist = ndist end end end end end Container = class('Container',Component) function Container:onInit() self.sprites = {384,385,386,387} self.atimer = 0 self.mtimer = 10 self.cframe = 1 end function Container:onAdded() self.xf = self.entity:getComp(Transform) self.rc = self.entity:getComp(RectCollider) self.tr = self.entity:getComp(TriRend) Timer:onTime(60,self.entity,function(ent) ent:destroy() end) end function Container:onUpdate() self.atimer = self.atimer + 1 if self.atimer > self.mtimer then self.atimer = 0 self.cframe = self.cframe + 1 if self.cframe > #self.sprites then self.cframe = 1 end self.tr.sprite = self.sprites[self.cframe] self.tr:updateUV() end local xfs = self.entity.scene:xfInRadius(self.xf.x,self.xf.y,30) for i = #xfs,1,-1 do if xfs[i] == nil or xfs[i].entity == nil then goto continue end local rc = xfs[i].entity:getComp(RectCollider) local sc = xfs[i].entity:getComp(ShipCtrl) if rc ~= nil and sc ~= nil then if rectOverlap(self.xf.x + self.rc.x1,self.xf.y + self.rc.y1, self.xf.x + self.rc.x2,self.xf.y + self.rc.y2, xfs[i].x + rc.x1,xfs[i].y + rc.y1, xfs[i].x + rc.x2,xfs[i].y + rc.y2) then sc:getContainer() if self.entity then self.entity:destroy() end end end ::continue:: end end --function Container:onDraw() -- local cam = self.entity.scene.cam -- local cx1,cy1 = cam:toSS((self.xf.x-4),(self.xf.y-4)) -- local cx2,cy2 = cam:toSS((self.xf.x+4),(self.xf.y+4)) -- spriteQuad(self.sprites[self.cframe],cx1,cy1,cx2,cy2,0,0) --end Bleeding = class('Bleeding',Component) function Bleeding:onInit() end function Bleeding:onAdded() self.xf = self.entity:getComp(Transform) local cent = self.entity.scene:getEnt("camera") local cam = cent:getComp(Camera) self.ps = PSystem({ maxPart = 20, lifetime = 80, rate = 10, emitCt = 1, x = 0, y = 0, vx = {-0.1,0.1}, vy = {-0.1,0.1}, emitfn = function (p) cam:pix(p[1],p[2],p[11].color) end, emitSett = {color=2}, affectors = {} }) self.ps.x,self.ps.y = self.xf.x,self.xf.y end function Bleeding:onUpdate() self.ps.x,self.ps.y = self.xf.x,self.xf.y self.ps:update() end function Bleeding:onDraw() self.ps:draw() end Teleport = class('Teleport',Component) function Teleport:onInit() self.sprites = {320,322,324,326,328} self.atimer = 0 self.mtimer = 5 self.cframe = 1 end function Teleport:onAdded() self.xf = self.entity:getComp(Transform) self.rc = self.entity:getComp(RectCollider) local cent = self.entity.scene:getEnt("camera") local cam = cent:getComp(Camera) self.ps = PSystem({ maxPart = 100, lifetime = 20, rate = 0, emitCt = 0, x = {-8,4}, y = {-8,4}, vx = {-0.5,0.5}, vy = {-0.5,0.5}, emitfn = function (p) local sett = p[11] local f = sett.frames cam:spr(f[(p[7] / p[8] * #f) // 1 + 1],p[1],p[2],0) end, emitSett = {frames={483,484,485}}, affectors = {} }) self.ps.x,self.ps.y = self.xf.x,self.xf.y self.ps:spawn(100) end function Teleport:onUpdate() self.ps:update() self.atimer = self.atimer + 1 if self.atimer > self.mtimer then self.atimer = 0 self.cframe = self.cframe + 1 if self.cframe > #self.sprites then self:destroy() end end end function Teleport:onDraw() local cam = self.entity.scene.cam local cx1,cy1 = cam:toSS((self.xf.x-10),(self.xf.y-10)) local cx2,cy2 = cam:toSS((self.xf.x+10),(self.xf.y+10)) spriteQuad(self.sprites[self.cframe],cx1,cy1,cx2,cy2,0,0,2,2) self.ps:draw() end Radar = class('Radar',Component) function Radar:onInit() self.blink = 0 self.x = 213 self.y = 110 end function Radar:onAdded() self.xf = self.entity:getComp(Transform) self.sc = self.entity:getComp(ShipCtrl) end function Radar:onUpdate() end function Radar:onDraw() local function nr(val) return val + math.random(0,49) end rectb(self.x - 25,self.y - 25,50,50,7) clip(self.x - 24,self.y - 24,48,48) if self.blink ~= 2 then self.blink = self.blink + 1 if self.sc.para then for i = 1,50 do pix(nr(self.x - 25),nr(self.y - 25),10) pix(nr(self.x - 25),nr(self.y - 25),10) pix(nr(self.x - 25),nr(self.y - 25),2) pix(nr(self.x - 25),nr(self.y - 25),5) end for i = 1,10 do local nx = nr(self.x - 25) line(nx,self.y - 25,nx,self.y + 24,7) end for i = 1,10 do local ny = nr(self.y - 25) line(self.x - 25,ny,self.x + 24,ny,7) end end clip() return end self.blink = 0 local gx = (self.xf.x // 100) * 100 - 140 local gy = ((self.xf.y) // 100) * 100 - 140 for iy = gy,gy + 500,100 do local py = (iy - self.xf.y) // 10 if py > -25 and py < 25 then line(self.x - 25,py + self.y,self.x + 24,py + self.y,7) end end for ix = gx,gx + 500,100 do local px = (ix - self.xf.x) // 10 if px > -25 and px < 25 then line(px + self.x,self.y - 25,px + self.x,self.y + 24,7) end end local xfs = self.entity.scene:xfInRadius(self.xf.x,self.xf.y,200) for i = 1,#xfs do local tf = xfs[i] local e = xfs[i].entity if e ~= nil then --tf = e:getComp(Transform) sc = e:getComp(ShipCtrl) ct = e:getComp(Container) bm = e:getComp(Bomb) bh = e:getComp(BlackHole) if tf ~= nil then local px,py = (tf.x - self.xf.x) // 10,(tf.y - self.xf.y) // 10 if sc ~= nil then pix(px + self.x,py + self.y,10) elseif bm ~= nil then pix(px + self.x,py + self.y,2) elseif ct ~= nil then pix(px + self.x,py + self.y,5) elseif bh ~= nil then circb(px + self.x,py + self.y,1,2) end end end end clip() end Sight = class('Sight',Component) function Sight:onInit() self.blink = 0 end function Sight:onAdded() self.xf = self.entity:getComp(Transform) local cent = self.entity.scene:getEnt("camera") self.cam = cent:getComp(Camera) end function Sight:onUpdate() end function Sight:onDraw() if self.blink == 3 then local lx = math.cos(math.rad(self.xf.rot)) * 150 + self.xf.x local ly = math.sin(math.rad(self.xf.rot)) * 150 + self.xf.y local x1,y1 = self.cam:toSS(self.xf.x,self.xf.y) local x2,y2 = self.cam:toSS(lx,ly) line(x1,y1,x2,y2,2) self.blink = 0 else self.blink = self.blink + 1 end end Label = class('Label',Component) function Label:onInit(text,offx,offy,color,small) self.offx = offx or 0 self.offy = offy or 1 self.color = color or 12 self.scale = 1 self.small = small or false self.fixed = false self.center = false self.text = text or "Default text" self.lastWidth = 0 end function Label:onAdded() self.xf = self.entity:getComp(Transform) self.cam = self.entity.scene:getEnt('camera'):getComp(Camera) end function Label:onUpdate() end function Label:onDraw() local cx = 0 if self.center then self.lastWidth = print(self.text,-99999,-99999,self.color,self.fixed,self.scale,self.small) cx = self.lastWidth / 2 end local px, py = self.cam:toSS(self.offx + self.xf.x - cx,self.offy + self.xf.y) self.lastWidth = print(self.text,px,py,self.color,self.fixed,self.scale,self.small) end DeathTimer = class('DeathTimer',Component) function DeathTimer:onInit(time) self.timer = time end function DeathTimer:onUpdate() self.timer = self.timer - Timer.delta if self.timer <= 0 then self.entity:destroy() end end ArenaCtrl = class('ArenaCtrl',Component) function ArenaCtrl:onInit() self.crange = {10,10,200,200} self.cmtime = 1000 self.ctime = 0 self.pilots = {} self.log = {} self.player_pilot = nil self.spawns = {} self.ranking = {} -- {ent=xxx,name="xxxx",id=12312,kills=0,deaths=0} end function ArenaCtrl:onUpdate() self.ctime = self.ctime + 1 if self.ctime > self.cmtime then self.ctime = 0 local nc = Factory.createContainer(self.entity.scene) local xf = nc:getComp(Transform) xf.x, xf.y = math.random(self.crange[1],self.crange[3]), math.random(self.crange[2],self.crange[4]) trace("Spawn container at ("..xf.x..","..xf.y..")") end for i = #self.log,1,-1 do self.log[i].time = self.log[i].time - Timer.delta if self.log[i].time <= 0 then table.remove(self.log,i) end end end function ArenaCtrl:addLog(tm,txt) self.log[#self.log+1] = {time = tm, text = txt} end function ArenaCtrl:onDraw() local ly = 120 for i = 1,#self.log do print(self.log[i].text,10,ly,2,false,1,true) ly = ly - 10 end end function ArenaCtrl:addPilot(pname) local np = { id = #self.pilots+1, name = pname, kills = 0, deaths = 0, score = 100, ship = nil, entity = nil, player = false, } self.pilots[#self.pilots+1] = np return np end function ArenaCtrl:getPilot(name) for i = 1,#self.pilots do if self.pilots[i].name == name then return self.pilots[i] end end end function ArenaCtrl:pilotsWithoutShips() local rv = {} for i = 1,#self.pilots do if not self.pilots[i].entity and not self.pilots[i].player then table.insert(rv,self.pilots[i]) end end return rv end function ArenaCtrl:spawnPilot(pilot_id,ship_type,x,y) local p = self.pilots[pilot_id] if not p then return end local inp = nil if p.player then inp = Input else inp = AI end ship_type = ship_type or p.ship local e = Factory.createShip(self.entity.scene,p,inp,ship_type) p.entity = e local xf = e:getComp(Transform) local sc = e:getComp(ShipCtrl) local sp = math.random(1,#self.spawns) if p.player then self.entity.scene.player = e self.entity.scene.main_ct = self.entity.scene.player:getComp(ShipCtrl) self.entity.scene.player:addComp(Radar) self.entity.scene.player:addComp(Sight) sc:teleport(self.spawns[sp].x * 8,self.spawns[sp].y * 8) else --sc:teleport(math.random(10,80) * 8,math.random(10,80) * 8) sc:teleport(self.spawns[sp].x * 8,self.spawns[sp].y * 8) end trace("Spawning "..e.name.." at "..xf.x..","..xf.y) return e end function ArenaCtrl:updateRankings() self.ranking = {} for k,v in ipairs(self.pilots) do self.ranking[k] = v end table.sort(self.ranking,function(l,r) if l ~= nil and r ~= nil then return l.score > r.score end end) end function ArenaCtrl:prepareActors(scene) -- cycle through the whole board and replace special sprites with stuff local mx, my = 0, 0 for my = 0,240 do for mx = 0,136 do local tile = mget(mx,my) if fget(tile,1) then if fget(tile,2) then -- TODO: Save spawn position table.insert(self.spawns,{x=mx,y=my}) mset(mx,my,mget(mx,my) - 1) elseif fget(tile,3) then local bh = scene:addEnt('bhole'..mx..","..my) bh:addComp(Transform,(mx*8) + 4,(my*8) + 4) bh:addComp(BlackHole) mset(mx,my,0) else mset(mx,my,0) end end end end end UIInput = class('UIInput',Component) function UIInput:onInit() self.holdtimer = 0 end game = Scene() function game:load() self.came = self:addEnt('camera') self.came:addComp(Transform) self.cam = self.came:addComp(Camera) ------- self.mr = self:addEnt("map") local mx = self.mr:addComp(Transform) self.mr:addComp(MapRend) ------- self.ace = self:addEnt('Arena Controller') self.arena = self.ace:addComp(ArenaCtrl) ----- self.arena.player_pilot = self.arena:addPilot("player") self.arena.player_pilot.player = true self.arena.player_pilot.ship = Prefs.ship self.arena:prepareActors(self) for j = 1,30 do local ni = math.random(1,#Names) local nn = Names[ni] table.remove(Names,ni) trace("Added pilot: " .. nn) local np = self.arena:addPilot(Names[math.random(1,#Names)]) np.ship = math.random(1,6) end --[[ Timer:onTime(1.0,self,function(ctx) local e = Factory.createPlayer(ctx) local xf = e:getComp(Transform) local sc = e:getComp(ShipCtrl) sc:teleport(100,100) ctx.cam:follow(xf) end) ]] local cte = Factory.createContainer(self) local cxf = cte:getComp(Transform) cxf.x, cxf.y = (15 * 8),(15 * 8) --So not ready lol --local bh = self:addEnt('bhole1') --bh:addComp(Transform,31*8,37*8) --bh:addComp(BlackHole) self.cam:follow(xf) trace("---------------LOADED--------------") --Timer:onTime(5.0,nil,function() trace("ONCE") end) Timer:onRepeat(2.0,nil,function() local ps = self.arena:pilotsWithoutShips() --trace("SHIPLESS: "..#ps) if #ps ~= 0 and Prefs.enemies then local e = self.arena:spawnPilot(ps[1].id,nil, math.random(10,80) * 8, math.random(10,80) * 8 ) end end) Timer:onRepeat(2.0,self,function(ctx) trace("Ents:"..#ctx.ents) end) end function game:enter(oscene) self.arena.player_pilot.ship = Prefs.ship if oscene == ssel and self.player == nil then local e = self.arena:spawnPilot(self.arena.player_pilot.id) local xf = e:getComp(Transform) local sc = e:getComp(ShipCtrl) --sc:teleport(100,100) e.scene.cam:follow(xf) end end function game:TIC() Timer:update() FPS:update() AniTiles.update(Timer.delta) cls() self:update() self:draw() FPS:draw() if keyp(11) and Prefs.debug then local ent = self.ents for i = 2,#self.arena.pilots do local pe = self.arena.pilots[i].entity if pe then local sc = pe:getComp(ShipCtrl) if sc and pe.name ~= "player" then trace("Destroying "..i..":"..ent[i].name) self.arena:addLog(2.0,pe.name.." suddenly exploded" ) sc:die('explode') end end end self.arena:addLog(2.0,"----MASS GENOCIDE----") end if keyp(7) and Prefs.debug then Prefs.god = not Prefs.god self.arena:addLog(2.0,"----GOD MODE TOGGLED----") end if keyp(5) and Prefs.debug then Prefs.enemies = not Prefs.enemies self.arena:addLog(2.0,"----ENEMIES TOGGLED----") end if key(4) and keyp(2) then Prefs.debug = not Prefs.debug self.arena:addLog(2.0,"----DEBUG TOGGLED----") end end function game:OVR() if self.main_ct ~= nil and self.main_ct.entity ~= nil then spr(192,0,0) for i=8,196,8 do spr(193,i,0) end spr(208,193,0) spr(194,200,0,-1,1,0,0,2,2) spr(194,224,0,-1,1,1,0,2,2) spr(195,216,0,-1,1,0,0,1,2) spr(209,192,8) local blen = ((self.main_ct.energy / self.main_ct.maxenergy) * 200) // 1 - 1 local bcol,scol = 2,1 if blen > 60 then bcol,scol = 4,3 end if blen > 120 then bcol,scol = 6,7 end if self.main_ct.safe then bcol,scol = 5,6 end if self.main_ct.para then bcol,scol = 10,9 if math.random(0, 3) == 1 then bcol = 11 end end rect(1,2,blen,3,bcol) rect(1,4,blen,1,scol) if self.main_ct.para then for i = 1,15 do pix(1 + math.random(0,math.max(0,blen - 1)), 2 + 2 + math.random(-2,-1),11) end end if self.main_ct.para then local chars = "1234567890-.abcdefghijklmnopqrstuvwxyz+ :!()" for lx = 0,1 do local cs = "" for ix = 1,9 do local rc = math.random(1,#chars) cs = cs .. chars:sub(rc,rc) end tinyprt(cs,201,2 + lx * 6) end else tinyprt(string.format("%04d",self.main_ct.energy),201,2) local pp = self.arena.player_pilot if pp then tinyprt(string.format("%03d",pp.kills)..":".. string.format("%03d",pp.deaths),201,8) end end else local an = (Timer.elapsed * 3) % 2 print("Press to change ship.",120,130,5) spr(238 + an,152,128) if btn(7) then SceneManager.push(ssel) end print("Rankings - Top 10",120,28,12) print("Name",120,36,12,false,1,true) print(" K : D",182,36,12,false,1,true) print("Points",217,36,12,false,1,true) for i=1,10 do local p = self.arena.ranking[i] if p then local col = 10 if p.name == self.arena.player_pilot.name then col = 11 end print(string.sub(p.name,1,15),120 + 1,35 + i * 8 + 1,15,false,1,true) print(string.sub(p.name,1,15),120,35 + i * 8,col,false,1,true) print("("..string.format("%03d",p.kills) .. ":" .. string.format("%03d",p.deaths) .. ")", 183,35 + i * 8 + 1,15,false,1,true) print("("..string.format("%03d",p.kills) .. ":" .. string.format("%03d",p.deaths) .. ")", 182,35 + i * 8,col,false,1,true) print(string.format("%04d p",p.score),219,35 + i * 8 + 1,15,false,1,true) print(string.format("%04d p",p.score),218,35 + i * 8,col,false,1,true) end end end end ssel = Scene() function ssel:load() self.came = self:addEnt('camera') local cxf = self.came:addComp(Transform) self.cam = self.came:addComp(Camera) cxf.x = 240 // 2 cxf.y = 0 // 2 self.a = 0 self.ships = {} local spacing = 30 local sw = #Ships * spacing for stype = 1, #Ships do local newe = self:addEnt(Ships[stype].name) local tf = newe:addComp(Transform,0,0,0,1) self.ships[stype] = tf tf.rot = 90 newe:addComp(Velocity) local tr = newe:addComp(TriRend,Ships[stype].sprite,2,2) tf.x = (((240//2) - (sw // 2)) + (stype) * spacing ) - 16 end self.stars = {} self.warp = false self.warptime = 0 self.rott = 0 self.wship = nil trace("SSEL LOADED") end function ssel:TIC() Timer:update() FPS:update() cls() self:update() self:draw() FPS:draw() local function genStar(index) local idx = index or #self.stars + 1 local rr = math.random(0,360) local dt = "pix" local dv = 1 local srad = 20 if self.warp then dt = "line" dv = 3 srad = 10 end local rx = math.cos(math.rad(rr)) * srad local ry = math.sin(math.rad(rr)) * srad self.stars[idx] = { a=rr, x=rx, y=ry, v=dv,t=0, ox=rx,oy=ry, d=dt } end local ms = 200 if self.warp then ms = 400 end if #self.stars < ms then genStar() genStar() if self.warp then genStar() genStar() genStar() genStar() genStar() genStar() genStar() end end if self.warp then for cr=math.max(10,self.warptime / 2),self.warptime * 2 do circ(240//2,136//2,cr,15-100//cr) end local tr = self.wship:getComp(TriRend) tr:onDraw() end for i = 1,#self.stars do local sf = self.stars[i] sf.x = sf.x + math.cos(math.rad(sf.a)) * sf.v sf.y = sf.y + math.sin(math.rad(sf.a)) * sf.v sf.t = sf.t + 1 sf.v = sf.v + 0.05 if sf.d == "line" then line(sf.ox + 240//2,sf.oy + 136//2,sf.x + 240//2,sf.y + 136//2,15-100//sf.t) else pix(sf.x + 240//2,sf.y + 136//2,15-100//sf.t) end if sf.t > 100 then genStar(i) end end if not self.warp then self.a = (self.a - 2) % 360 local lx,ly = 0,0 for ix = 15,10,-1 do --lx = math.cos(math.rad((self.a + ix * 2) % 360)) * (20 - ix / 2) ly = math.sin(math.rad((self.a + ix * 2) % 360) * 2) * (10 - ix / 2) printc("SELECT A SHIP",240//2 + lx,10 + ly,ix,true,2,false) end for ix = 10,10,-1 do lx = math.cos(math.rad((self.a * 5 + ix * 50) % 360)) * (ix / 5) ly = math.sin(math.rad((self.a * 5 + ix * 50) % 360)) * (ix / 5) --printc(Ships[Prefs.ship].name,240//2+lx,100+ly,2 - ix,true,2,false) --printc(Ships[Prefs.ship].desc,240//2+lx,120+ly,2 - ix,true,1,false) end printc(Ships[Prefs.ship].name,240//2+1,100+1,15,true,2,false) printc(Ships[Prefs.ship].name,240//2,100,2,true,2,false) printc(Ships[Prefs.ship].desc,240//2+1,120+1,15,true,1,false) printc(Ships[Prefs.ship].desc,240//2,120,2,true,1,false) --for k,v in ipairs(self.ships) do -- if k ~= Prefs.ship then -- v.rot = self.rott // 45 * 45 -- end --end if btnp(2) then self.ships[Prefs.ship].scale = 1 self.ships[Prefs.ship].rot = 90 --self.ships[Prefs.ship].entity:getComp(Label):setAwake(false) Prefs.ship = Prefs.ship - 1 if Prefs.ship < 1 then Prefs.ship = #self.ships end --self.ships[Prefs.ship].entity:getComp(Label):setAwake(true) end if btnp(3) then self.ships[Prefs.ship].scale = 1 self.ships[Prefs.ship].rot = 90 --self.ships[Prefs.ship].entity:getComp(Label):setAwake(false) Prefs.ship = Prefs.ship + 1 if Prefs.ship > #self.ships then Prefs.ship = 1 end --self.ships[Prefs.ship].entity:getComp(Label):setAwake(true) end if btnp(4) then self.warptime = 0 self.warp = true --for k,v in ipairs(self.ships) do -- v.entity:destroy() --end --self.ships = {} self.wship = self:addEnt("Warpship") local tf = self.wship:addComp(Transform,240/2,0,0,1) tf.rot = 90 local v = self.wship:addComp(Velocity) v.rot = 1 self.wship:addComp(TriRend,Ships[Prefs.ship].sprite,2,2) end else self.warptime = self.warptime + 2 local t = self.wship:getComp(Transform) local v = self.wship:getComp(Velocity) v.rot = v.rot + 1 t.rot = t.rot + v.rot if self.warptime > 120 then self.warp = false self.warptime = 0 self.stars = {} self.wship:destroy() self.wship = nil SceneManager.pop() end end self.rott = self.rott + 1 --if keyp(19,10,2) then -- self.rott = self.rott + 1 --elseif keyp(1,10,2) then -- self.rott = self.rott - 1 --end --print(self.rott,5,7,11) if self.rott > 360 then self.rott = 0 end if #self.ships > 0 then self.ships[Prefs.ship].scale = 2 self.ships[Prefs.ship].rot = self.rott end end function ssel:OVR() end AniTiles.build() SceneManager.push(game) SceneManager.push(ssel) function TIC() SceneManager.TIC() end function OVR() SceneManager.OVR() end function SCN(line) SceneManager.SCN(line) end