0 ??????????????????$$% % &&////////////////////////   ?HHHHHHHIK K L L M MNNOOOOOOOOOOOOOO    % 4DS b r qq+6'`(&@(k=h0' 30V? wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww         𰐐𰰰𰰰          𰰰𐐐𐐐     𰰰𰰰𰰰𰰰𰰰𰰰𐐐        𠐐 𐰐𐰐        𰐐а𰰰𰰰𰰰𰰰@@            𰰰𐐐𐐐𐰐𐰐𰰰 wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 𰰰wpw ppwwpp wwp p𰰰p pp pp pp p𰰰p pppp pp p p  p    p p   p   p  p 𰰰p ̼̼̻̼̼̻̼̻̼̻̻p̼̼̻̼̼̻̼̻̼̻̻p̼̼̻̼̼̻̼̻̼̻̻p̼̼̻̼̻̻̼̻̼̻̻p ̼̻̼̻̼̻̼̼̻̻p ̼̻̼˼̻̻̼̻̼̼̻̻p ̼̻̼˼̻̻̼̻̼̼̻̻p ̼̻̼̻̻̼̻̼̼̻̻p  ̼̻̼̻̻̼̻̼̼̻̻p ̼̻̼̻̻̼̻̼̼̻̻p̼̻̼̻̻̼̻̼̼̻̻p 000̼̻̼̻̻̼̻̼̻̻p̼̻̼̻̼̻̼̻̻p ̼̼̻̼̼̻̼̻̼̻̻p   ̼̼̻̼̼̻̼̻̼̻̻p  ̼̼̻̼˼̻̻̻̻̼̻̻p ̼̼̻̼˼̻̻̻̻̼̻̻p ̼̼̻̼˼̻̻̻̻̼̻̻p   ̼̼̻̼˼̻̻̻̻̼̻̻p   ̼̼̻̼˼̻̻̻̻̼̻̻p ̼̼̻̼˼̻̻̻̻̼̻̻p p  ww  `𐠠p {p ww  yp   wpw wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww 𰰰𰰰    ``𐠐    @@𰰰        𐰐      𰰰 𰰰𰰰         wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww %-- title: Legendary Champion: Rebirth -- author: DuckAfire -- desc: The rebirth of 'Legendary AMONGUS'! -- license: AGPL-3.0-or-later -- page: duckafire.github.io/nest -- version: 3.0.2 -- input: mouse -- script: lua -- saveid: legendary_champion_rebirth_save_id -- Legendary Champion: Rebirth -- Copyright (C) 2024 Duckafire -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU Affero General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU Affero General Public License for more details. -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . ------------------------------------------------------------ ----------------------- TINY LIBRARY ----------------------- ------------------------------------------------------------ -- Zlib License -- -- TinyLibrary -- Copyright (C) 2024 DuckAfire -- -- This software is provided 'as-is', without any express or implied -- warranty. In no event will the authors be held liable for any damages -- arising from the use of this software. -- -- Permission is granted to anyone to use this software for any purpose, -- including commercial applications, and to alter it and redistribute it -- freely, subject to the following restrictions: -- -- 1. The origin of this software must not be misrepresented; you must not -- claim that you wrote the original software. If you use this software -- in a product, an acknowledgment in the product documentation would be -- appreciated but is not required. -- 2. Altered source versions must be plainly marked as such, and must not be -- misrepresented as being the original software. -- 3. This notice may not be removed or altered from any source distribution. local TL1, TL2, TL4 = {}, {}, {} do local T63,E3,TU6,MS22,F33,M42,T21,MM12,MM14,M43,MA0=trace,error,table.unpack,math.sqrt,fget,mget,type,math.max,math.min,mouse,math.abs local function libError(a,b,c,d,e,f)if a~=nil then if not a then return end end local g={"Error","Function","Index"}local h={nil,e,"#"..f}local i="\n\n[coli2DA]"b=b and'"'..b..'" ' or"" local function j(k)h[1]=b..k end if c=="1" then j("was not specified")elseif c=="2" then j("was not defined")elseif c=="3" then j("is invalid")else j(c)end for l=1,3 do i=i.."\n"..g[l]..": "..h[l].."." if l==1 and d~=nil then i=i.."\nTry: " for m=1,#d-1 do i=i..d[m].." | " end i=i..d[#d]end end T63("\n>\n>\n>")E3(i.."\n")end local RE="rect" local CI="circ" local SI="simp" local W="width" local H="height" local R="radius" local ByBd=1 local OrgBd={"newBody","distance","mapAlign","tile","tileCross","rectangle","circle","shapesMix","impactPixel"} TL1.newBody=function(a,b,c,d,e)local f=nil if a==RE then f={x=b,y=c,width=d or 8,height=e or 8}elseif a==CI then f={x=b,y=c,radius=d or 4}elseif a==SI then f={x=b,y=c}end libError(f==nil,"type","3",{RE,CI,SI},OrgBd[ByBd],1)local g={"x","y"}for h=1,2 do libError(f[g[h]]==nil,g[h],"3",nil,OrgBd[ByBd],1+h)end return f end local function ckbd(a,b,c) ByBd=c local d,e,f d={}e={"x","y","width","height"}f={[RE]=4,[CI]=3,[SI]=2}for g=1,#a do d[g]={}for h=1,f[b[g]]do if b[g]==CI and h==3 and a[g][R]~=nil then d[g][h]=a[g][R]else d[g][h]=a[g][e[h]]or a[g][h]end end end local i={}for g=1,#a do i[g]=TL1.newBody(b[g],d[g].x or d[g][1],d[g].y or d[g][2],d[g][W]or d[g][R]or d[g][3],d[g][H]or d[g][4])end ByBd=1 return TU6(i) end TL1.distance=function(a,b)a,b=ckbd({a,b},{SI,SI},2)return MS22((a.x-b.x)^2+(a.y-b.y)^2) end TL1.mapAlign=function(a,b)a=ckbd({a},{(b)and RE or SI},3)if b then a[W]=a[W]or 8 a[H]=a[H]or 8 a.x=a.x+a[W]//2 a.y=a.y+a[H]//2 end return(a.x//8)*8,(a.y//8)*8 end TL1.tile=function(a,b,c,d,e)a=ckbd({a},{RE},4)d=d or 0 e=e or 0 local f,g,h,i local j,k=a[W],a[H]if b=="top" then f,g,h,i=0,-1,j-1,-1 elseif b=="bottom" then f,g,h,i=0,k,j-1,k elseif b=="left" then f,g,h,i=-1,0,-1,k-1 elseif b=="right" then f,g,h,i=j,0,j,k-1 else libError(nil,"type","3",{"top","bottom","left","right"},"tile","(last)")end return F33(M42((a.x+f)//8+d,(a.y+g)//8+e),c)and F33(M42((a.x+h)//8+d,(a.y+i)//8+e),c)end TL1.tileCross=function(a,b,c,d,e)a=ckbd({a},{RE},5)local f={}for g=1,4 do f[g]=(T21(c)=="number")and c or(T21(c)~="table")and 0 or c[g]end local h={}local i={"top","bottom","left","right"}for g=1,4 do h[g-1]=TL1.tile(a,i[g],f[g],d,e)end return(b)and{top=h[0],bottom=h[1],left=h[2],right=h[3]}or h end TL1.box360=function(a,b,c,d,e,f)local g=TL1.tileCross(c,false,d,e,f)local h=b and((b<0 and g[0])or(b>0 and g[1]))or false local i=a and((a<0 and g[2])or(a>0 and g[3]))or false return h or i end TL1.rectangle=function(a,b)local c,d={a,b},{W,H}for e=1,2 do for f=3,4 do if not c[e][f]and not c[e][d[f]]then c[e][f]=1 end end end c[1],c[2]=ckbd({c[1],c[2]},{RE,RE},6)return MM12(c[1].x,c[2].x)=a and f.x<=c and f.y+f[H]-1>=b and f.y<=d end TL1.impactPixel=function(a,b,c,d)libError(a~=RE and a~=CI,"type","3",{RE,CI},OrgBd[9],"(first)")b,c=ckbd({b,c},{a,a},9)if a==RE then if not d and not TL1.rectangle(b,c)then return nil,nil end local e={x=b.x+b[W]/2,y=b.y+b[H]/2,[R]=(b[W]+b[H])/2}local f={x=c.x+c[W]/2,y=c.y+c[H]/2,[R]=(c[W]+c[H])/2}return TL1.impactPixel(CI,e,f,true)end if a==CI then if not d and not TL1.circle(b,c)then return nil,nil end local g=(b.x*c[R])+(c.x*b[R])local h=(b.y*c[R])+(c.y*b[R])local i=(b[R]+c[R])return g/i,h/i end end end do local T63,E3,P11,T20,T21,MA0,SF3,T19,SS14,S60=trace,error,print,tostring,type,math.abs,string.find,tonumber,string.sub,spr local function libError(a,b,c,d,e,f)if a~=nil then if not a then return end end local g={"Error","Function","Index"}local h={nil,e,"#"..f}local i="\n\n[print+]"b=b and'"'..b..'" ' or"" local function j(k)h[1]=b..k end if c=="1" then j("was not specified")elseif c=="2" then j("was not defined")elseif c=="3" then j("is invalid")else j(c)end for l=1,3 do i=i.."\n"..g[l]..": "..h[l].."." if l==1 and d~=nil then i=i.."\nTry: " for m=1,#d-1 do i=i..d[m].." | " end i=i..d[#d]end end T63("\n>\n>\n>")E3(i.."\n")end local LenBy=1 local ShaBy=1 TL2.length=function(a,b,c,d)local e={"length","center","pCenter","pShadow","pBorder","pList"}LenBy=1 c=c or 1 return P11(T20(a),0,136,0,b,c,d),5*c end TL2.center=function(a,b,c,d,e,f)if LenBy<2 then LenBy=2 end local g,h=TL2.length(a,d,e,f)return(b or 0)-g//2+1,(c or 0)-h//2 end TL2.pCenter=function(a,b,c,d,e,f,g)LenBy=3 b,c=TL2.center(a,b,c,e,f,g)P11(a,b,c,d,e,f or 1,g)end TL2.pShadow=function(a,b,c,d,e,f,g,h,i)local j=(ShaBy==1)and"pShadow" or"pList" local k={}if T21(b)=="number" then k[1]=b<0 and 0 or b>15 and 15 or MA0(b)k[1]=k[1].."-1" elseif T21(b)=="table" then for l=1,#b do k[l]=b[l]end else libError(nil,"shadow","is not a number or table",nil,j,5)end libError(k[1]==nil,"shadow","values not defined",nil,j,1)if i then LenBy=4 c,d=TL2.center(a,c,d,f,g,h)end g=g or 1 local m,n,o=0,0,0 local p,q,r={},{},{}local s={p,q,r,"color","direction","distance"}local t={[0]={0,-1},{0,1},{-1,0},{1,0}} for l=1,((#k<4)and#k or 4)do for u=1,2 do m=SF3(k[l],'-')if#k[l]==1 and T19(k[l])~=nil then m=0 end libError(m==nil,nil,"Hyphen not specified. In index #"..l..' of the table "shadow"',nil,j,1)if u==1 then p[l]=SS14(k[l],1,m-1) k[l]=SS14(k[l],m+1,#k[l]) else q[l]=SS14(k[l],1,m-1) if m==0 then r[l]=g else r[l]=SS14(k[l],m+1,#k[l]) end end for v=1,3 do if s[v][l]~=nil then s[v][l]=T19(s[v][l])end end end for u=1,3 do libError(T21(T19(s[u][l]))~="number",nil,"The element "..s[u+3]..' is NaN. In index #'..l..' of the table "shadow".',nil,j,1)end for v=2,3 do s[v][l]=(s[v][l]<0)and 0 or(s[v][l]>3)and 3 or s[v][l]end n=c+t[q[l]][1]*r[l]o=d+t[q[l]][2]*r[l]P11(a,n,o,p[l],f,g,h)end P11(a,c,d,e,f,g,h)ShaBy=1 end TL2.pBorder=function(a,b,c,d,e,f,g,h,i,j)local k,l=0,0 local m={{0,-1},{0,1},{-1,0},{1,0}}if j then LenBy=5 b,c=TL2.center(a,b,c,f,g,i)end g=g or 1 n=e or 0 h=h or g for o=1,4 do k=b+m[o][1]*h l=c+m[o][2]*h P11(a,k,l,e,f,g,i)end P11(a,b,c,d,f,g,i)end TL2.pList=function(a,b,c,d,e,f,g,h,i,j,k)libError(T21(a)~="table","text","1",nil,"pList",1)local l={}if T21(d)=="table" then libError(#d<#a,"color","insufficient number of indexes","pList",3)l=d else for m=1,#a do l[m]=d end end b,c=b or 0,c or 0 g=g or 1 e=e or 13*g i=i or-1 if k then libError(T21(k)~="table","effect","3",nil,"pList",10)libError(k[1]~="shadow" and k[1]~="border","effect[1]","3",{"shadow","border"},"pList",10) if k[1]=="shadow" then libError(T21(k[2])~="table" and T21(k[2])~="number","effect[2]","is not a table or number",nil,"pList",10)elseif k[1]=="border" then libError(T21(k[2])~="number","effect[2]","is NaN",nil,"pList",10) end end local n=0 if j or i~=-1 then local o,p=0,0 for m=1,#a do if o<#a[m]then o=#a[m]p=a[m]end end LenBy=6 if j then b=TL2.center(p,b,c-#a,f,g,h)c=c-((#a-1)*e*g)//2 end n=TL2.length(p,f,g,h)end local q,r=0,0 for m=1,#a do q=b r=c+e*(m-1)if i~=-1 then LenBy=6 if i==0 then q=TL2.center(a[m],q+n//2,r,f,g,h)elseif i==1 then q=q+n-TL2.length(a[m],f,g,h)end end if not k then P11(T20(a[m]),q,r,l[m],f,g,h)else if k[1]=="shadow" then ShaBy=2 TL2.pShadow(a[m],k[2],q,r,l[m],f,g,h)elseif k[1]=="border" then TL2.pBorder(a[m],q,r,l[m],k[2],f,g,k[3],h)end end end end TL2.title=function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o)libError(T21(p)=="table",'"sprites"',"1",nil,"title",1)local q,r,s={},{},0 if T21(i)~="table" then s=i or 0 for t=1,#a do q[t]=s end else q=i end if T21(j)~="table" then s=j or 0 for t=1,#a do r[t]=s end else r=j end local function u(v,w,x)libError(#a>#v,w,"has a insuficient quatity of indexes","title",x)end u(q,"flip",9)u(r,"rotate",10)local y=nil if T21(d)=="number" then y={}for t=1,#a do y[t]=d end elseif T21(d)=="table" then u(d,"chromaKey",4)y=d else y={}for t=1,#a do y[t]=0 end end b,c=b or 0,c or 0 f=f or 1 e=e or 1 n=n or 1 o=o or 1 g=(g or 8)*f h=(h or 8)*f local z,aa=0,0 if k then z=(m)and(g//2)or((g+e)*#a-e)//2 end if l then aa=(m)and(((h+e)*#a-e)//2)or(h//2)end local ab,ac for t=1,#a do if m then ab=b-z ac=c+((h+e)*(t-1))-aa else ab=b+((g+e)*(t-1))-z ac=c-aa end S60(a[t],ab,ac,y[t],f,q[t],r[t],n,o)end end end do local T63,E3,P51,T19,SS14,T20,T21,SF3,TI1=trace,error,pmem,tonumber,string.sub,tostring,type,string.find,table.insert local function libError(a,b,c,d,e,f)if a~=nil then if not a then return end end local g={"Error","Function","Index"}local h={nil,e,"#"..f}local i="\n\n[longBit]"b=b and'"'..b..'" ' or"" local function j(k)h[1]=b..k end if c=="1" then j("was not specified")elseif c=="2" then j("was not defined")elseif c=="3" then j("is invalid")else j(c)end for l=1,3 do i=i.."\n"..g[l]..": "..h[l].."." if l==1 and d~=nil then i=i.."\nTry: " for m=1,#d-1 do i=i..d[m].." | " end i=i..d[#d]end end T63("\n>\n>\n>")E3(i.."\n")end local ZERO=2000000000 local MAX=2999999999 local LBC={} local CID={} local Origin={"getNum","getBool","setNum","setAll","update"}local GetBy=1 local SetBy=3 local AllBy=4 local function classToId(a,b,c)for d=1,#CID do if a==LBC[CID[d]]then return CID[d]end end libError(nil,nil,"Invalid class ("..a..")",nil,b,c)end local function ckSign(a,b,c)libError(a<0,nil,"It is not possible store negative values",nil,b,c)end local function ckId(a,b,c,d)a=(d~=2)and a or classToId(a,b,c)libError(a<0 or a>((d==1)and 255 or 9),"Index","3",nil,b,c)end local function ckDf(a,b,c)libError(P51(a)MAX,nil,"Memory not defined or invalid",nil,b,c)end local function ckGet(a,b,c,d)b=b or 1 libError(b<=0,"length","3",nil,c,d+2)local e={a+1,a+1+b-1}for f=1,2 do libError(e[f]<2-1*(f-1)or e[f]>10,((f==1)and"index" or"length"),"3",nil,c,d+2*(f-1))end return e[1],e[2] end TL4.getNum=function(a,b,c)ckId(a,Origin[GetBy],1,0)local d=classToId(b,"getNum",2)a,c=ckGet(a,c,Origin[GetBy],1)GetBy=1 return T19(SS14(T20(P51(d)),a,c))end TL4.getBool=function(a,b,c,d)GetBy=2 return TL4.getNum(a,b,d)==(c or 1)end TL4.getClass=function(a)ckId(a,"getClass",1,1)return LBC[a]end TL4.getAll=function(a,b)local c=classToId(a,"getAll",1)ckDf(c,"getAll",1)local d=SS14(T20(P51(c)),2)if b then return d end return T20(T19(d)) end TL4.setClass=function(a,b,c)c=c or 0 libError(T21(a)~="table","classes","1",nil,"setClass",1)local d=c+#a-1 libError(d>255,nil,"Index overflow (init + #classes - 1 > 255)",nil,"setClass",1)local e=1 local f=1 local g=false local h=false for i=c,d do libError(a[e]=="" or SF3(a[e]," ")~=nil,nil,"Invalid class. Do not use void strings or strings with spaces",nil,"setClass",1)h=(LBC[i]~=nil)if not h or b then LBC[i]=a[e] f=f+1 end e=e+1 g=true if not h then for j=1,#CID do if CID[j]==i then g=false break end end end if g then TI1(CID,i)end end return(#a==f)end TL4.setNum=function(a,b,c,d)local e,f="",0 b,d=ckGet(b,d,Origin[SetBy],2)if T21(a)=="boolean" then g=(a)and 1 or 0 else g=T20(a)local h=(d+1)-b if h>1 then for i=1,h do if#g10^h,nil,"Sub-memory OVERFLOW",nil,Origin[SetBy],1)end ckSign(f,Origin[SetBy],1)local j=classToId(c,Origin[SetBy],3)local function k(l,m)return SS14(T20(P51(j)),l,m)end local n=k(1,b-1)local o=k(d+1)SetBy=3 P51(j,T19(n..g..o))end TL4.setAll=function(a,b,c,d)if not c then ckSign(a,Origin[AllBy],1)end local e=classToId(b,Origin[AllBy],2)if c then a=P51(e)+a else a=T20(a) if#a<9 then for f=1,9-#a do a="0"..a end end a=T19("2"..a)end local g=(aMAX)and 1 or 0 if h then if g<0 then P51(e,ZERO) elseif g>0 then P51(e,MAX) else P51(e,a) end else libError(g<0,nil,"[ UNDERFLOW ]",nil,"setAll",1)libError(g>0,nil,"[ OVERFLOW ]",nil,"setAll",1)P51(e,a)end AllBy=4 return g end TL4.boot=function(a,b,c,d,e)c=c or 0 e=(T19(e)~=nil)and e or 0 local f,g,h=0,"",0 ckId(c,"boot",3,1)ckId(e,"boot",5,0) local i=c+#a-1 libError(#a>256,nil,"The table specified is bigger that 256",nil,"boot",1)libError(i>255,nil,"Index overflow (init + #items - 1 > 255)",nil,"boot",1)for j=c,i do f=f+1 if P51(j)<=ZERO or b then g=a[f]h=T19(g)ckSign(h,"boot",1)libError(T21(g)~="string",nil,g.." is not a string",nil,"boot",1)libError(T19(h)==nil,nil,g.." cannot be converted to number",nil,"boot",1)libError(T19(h)>MAX,nil,g.." is too large (max: "..MAX..")",nil,"boot",1)while#g<9 do g=(d)and e..g or g..e end P51(j,T19("2"..g))end end return(#a==f)end TL4.update=function(a,b,c)ckDf(classToId(a,"update",2),"update",2)local d={#b,#c,'"values"','"indexes"'}for e=1,2 do libError(d[e]<1 or d[e]>9,d[e+2],"invalid quantity of indexes",{"(min: 1","max: 9)"},"update",1+e)end libError(#b~=#c,nil,"The quantity of values and indexes must be equal",nil,"update","2-3")GetBy,SetBy=5,5 local f,g,h="",0,0 for e=1,#c do f="values["..e.."] is not a "g=T19(b[e])libError(T21(b[e])~="string",nil,f.."string",nil,"update",1)libError(g==nil,nil,f.."number",nil,"update",1)if TL4.getNum(c[e],a,#b[e])~=g then TL4.setNum(g,c[e],a,#b[e])h=h+1 end end return h end TL4.clear=function(a,b,c,d)local e=false local f={"all","memory","class","noMemory","noClass"}for g=1,#f do if a==f[g]then e=true break end end libError(not e,"type","3",f,"clear",1)c=c or 0 d=d or 255 ckId(c,"clear",3,0) ckId(d,"clear",4,1) local h,i=false,0 local function j()i,h=i+1,true end local k=(b)and 0 or ZERO if a=="memory" or a=="all" then for g=c,d do P51(g,k)j()end end if a=="class" or a=="all" then for g=#CID,1,-1 do if CID[g]>=c and CID[g]<=d then LBC[CID[g]]=nil CID[g]=nil j()end end return h end if a=="noClass" then for g=c,d do if LBC[g]==nil then P51(g,k)j()end end return h end if a=="noMemory" then local l=0 for g=#CID,1,-1 do l=CID[g]if l>=c and l<=d and(P51(l)MAX)then LBC[l]=nil CID[g]=nil j()end end return h,i end end TL4.swapClass=function(a,b)local function c(d,e,f)libError(T21(d)~="string",e,"3",nil,"swapClass",f)end c(a,"newName",1)c(b,"oldName",2)LBC[classToId(b,"swapClass",2)]=a end end local coli, plus, lbit = TL1, TL2, TL4 -- [Cartridge] https://tic80.com/play?cart=3288 -- [Project--] https://github.com/duckafire/TinyLibrary --========================================================-- --====================== GLOBALS =========================-- --========================================================-- -- common local Menu = 0 local Sus = math.random(100) local Volume = {nil, nil} local FirstRun = false -- about "welcome" local WelcomeID = 1 local Winner = false local TrophyY = 32 local TrophyAdd = 0.1 local LuckyBox = false -- this skin is available local PortalBuffer = {} -- store entities that will be removed by a portal local PortalCool = 0 -- countdown and cooldown local BtnScaleTimer = 0 -- for show buttons hitbox, after swap buttom "mode" -- for menu animation local TextAnimID = 1.0 -- indexes local TextAnimCO = 8.0 local TextAnimUP = false local BtnTop = 14.0 -- sprites local BtnBottom = 30.0 local BtnLeft = 46.0 local BtnRight = 46.0 local BigBtnMode = false local BigBtnTim = 0.0 -- configuration -- entities from other objects local Attacks = {back = {}, front = {}} local Effects = {back = {}, front = {}} --========================================================-- --======================= TOOLS ==========================-- --========================================================-- local function ConfigPacks(isPhone) if isPhone then lbit.setNum(2, 5, "config1") lbit.setNum(2, 6, "config1") lbit.setNum(3, 7, "config1") lbit.setNum(2, 9, "config1") lbit.setNum(2, 1, "config2") lbit.update("joystick1", {"029106052"}, {1}) lbit.update("joystick2", {"25123"}, {1}) lbit.update("attack", {"226122092"}, {1}) else lbit.setNum(1, 5, "config1") lbit.setNum(1, 6, "config1") lbit.setNum(1, 7, "config1") lbit.setNum(1, 9, "config1") lbit.setNum(1, 1, "config2") lbit.update("joystick1", {"040090053"}, {1}) lbit.update("joystick2", {"05311"}, {1}) lbit.update("attack", {"200090053"}, {1}) end Language:updateAll() Joystick.ctrl:boot(true) Coup:boot() end local function TextAnim(txt, invert) if txt then local char1, char2 = ">", "<" if invert then char1, char2 = "<", ">" end local tbl = {char1.." "..txt.." "..char2, char1..txt..char2} return tbl[math.floor(TextAnimID)] end return math.floor(TextAnimCO) end local function NextNum(num, min, max, invert) if invert then return (num > min) and num - 1 or max end return (num < max) and num + 1 or min end local function NextSubMem(id, class, len, max, min, invert) local num = 0 num = lbit.getNum(id, class, len) num = NextNum(num, min or 1, max, invert) lbit.setNum(num, id, class, len) end local function SfxArg(all) local id, note, channel = table.unpack(all) return id, note, -1, channel, Volume[2] end local function Sound(id) -- CHANNELS: -- 0: player (menus) -- 1: entities -- 2: visual effects -- 3: boss local content = { -- index, note, channel {00, "E-6", 0}, -- 01: click {01, "F-6", 0}, -- 02: start match {02, "E-5", 0}, -- 03: match countdown (3/2/1) {02, "E-6", 0}, -- 04: ~ (GO!) {03, "F-5", 0}, -- 05: save coin {04, "D#6", 1}, -- 06: consumable collected {05, "F-5", 2}, -- 07: achievement getted {06, "C-4", 0}, -- 08: damage {07, "F-3", 2}, -- 09: explosion {08, "C#4", 1}, -- 10: trap alert {09, "C#3", 3}, -- 11: Supervisor starting {10, "C#4", 3}, -- 12: supervisor shot } sfx(SfxArg(content[id])) end local function GetCorners(obj, plus) plus = plus or 0 return { {obj.x + 1 + plus, obj.y + plus}, -- top-left {obj.x + 6 - plus, obj.y + plus}, -- top-right {obj.x + 1 + plus, obj.y + 7 - plus}, -- bottom-left {obj.x + 6 - plus, obj.y + 7 - plus}, -- bottom-right } end local function SaveStatus() Language:backup("states", true) Coins:save() -- add one lbit.setAll(1, ((Winner) and "victories" or "defeats"), true, true) -- add one or clear it lbit.setAll(((Winner) and 1 or 0), "sequences", Winner, true) Language:update("stat") end local function ClearPortalBuffer() if #PortalBuffer == 0 then return end local qtt = Enemies.quantity local def = Enemies.default table.sort(PortalBuffer) local id = 0 for i = #PortalBuffer, 1, -1 do id = PortalBuffer[i] if def[id].removing == -1 then qtt[def[id].name] = qtt[def[id].name] - 1 table.remove(Enemies.default, id) table.remove(PortalBuffer) end end end local function ClearTable(tbl, last) for i = #tbl, last or 1, -1 do tbl[i] = nil end end local function MatchStart() Achiev[1] = 0 Achiev[5] = 0 Winner = false Consu.cool = 600 Coins:boot() Player:boot() Joystick.ctrl:boot() Timer:boot() Sash:boot() Map:boot(true) Boss:set() PortalCool = 0 Enemies.cool = (Map.curMap == 3) and math.random(300, 600) or math.random(60, 180) end local function MatchCountdown(update) if Timer.start == 0 then return false end if update then Timer.start = Timer.start - 1 if Timer.start == 0 then Timer.on = true Portal(true, true) end return true end -- 3, 2, 1 if Timer.start >= 60 then if Timer.start >= 120 and Timer.start < 240 and Timer.start % 60 == 0 then Sound(3) end local color, colorb = {15, 2, 4}, {14, 1, 3} local id = Timer.start // 60 elli( 120, 68, 27, 32, color[ id]) ellib(120, 68, 27, 32, colorb[id]) spr(164 - id + 1, 88, 36, 0, 8) return false end -- GO! if Timer.start == 59 then Sound(4) end elli( 120, 66, 70, 41, 6) ellib(120, 66, 70, 41, 5) spr(160, 56, 34, 0, 8, 0, 0, 2, 1) return false end local function MatchEnd() -- common Achiev:progress(6) -- game border poke(0x03ff8, 0) -- delete entities local entities = { Attacks.back, Attacks.front, Boss.attacks.back, Boss.attacks.front, Consu.entities, Doors.back, Doors.front, Effects.back, Effects.front, Enemies.default, Enemies.kamikaze, Traps.back, Traps.front, } for i = 1, #entities do ClearTable(entities[i]) end -- reset enemies count local tags = Enemies.tags[Map.curTheme] for i = 1, #tags.default do Enemies.quantity[tags.default[i]] = 0 end for i = 1, #tags.kamikaze do Enemies.quantity[tags.kamikaze[i]] = 0 end collectgarbage() end --========================================================-- --====================== EFFECTS =========================-- --========================================================-- -- LIST (a-z): -- NewExplosion -> create an explosion effect that can hit player -- NewSmoke -> to hide enemies before them spawn in Theme-01 -- Portal -> used to remove entities from map, in match _G.NewExplosion = function(x, y, r) -- explosion local it = { x = x, y = y, radius = r, timer = 10 } function it:update(i) if self.timer > 0 then self.timer = self.timer - 1 Player:hitbox(self) return end table.remove(Effects.front, i) end function it:draw() circ(self.x, self.y, self.radius * 0.8, 7) end -- smoke local angle, dist, radius = 0.0, 0.0, 0 local lSpeed, lSmoke, it2 = 0, nil, nil for i = 1, math.random(16, 24) do dist = ((it.radius * 1.1) * (math.random(5) / 10)) angle = math.rad(math.random(360)) radius = math.random(math.floor(it.radius * 0.25), it.radius // 2) -- 25-50% isSmoke = (math.random(3) == 1) -- fire or smoke particle lessSpd = (isSmoke) and 2 or 1 it2 = { x = it.x + math.cos(angle) * dist, y = it.y + math.sin(angle) * dist, sx = ((math.random(2, 4) / 10) * math.cos(angle)) / lessSpd, sy = ((math.random(2, 4) / 10) * math.sin(angle)) / lessSpd, radius = radius, maxRad = radius, color = (isSmoke) and {10, 11} or {12, 3, 1, 2}, id = (isSmoke) and math.random(2) or 1, lessRad = math.random(5, 8) / 40, timer = 10, } function it2:update(i) self.x = self.x + self.sx self.y = self.y + self.sy if self.timer == 0 and self.radius > -1 then self.radius = self.radius - self.lessRad end if self.timer > 0 then self.timer = self.timer - 1 return end if self.radius < 0 then table.remove(Effects.front, i) return end if #self.color > 2 and self.radius < self.maxRad * (0.8 - 0.2 * (self.id - 1)) then self.id = self.id + 1 end end function it2:draw() local color = (self.timer > 0) and 7 or self.color[self.id] circ(self.x, self.y, self.radius, color) -- smoke end table.insert(Effects.front, it2) end Sound(9) table.insert(Effects.front, it) end _G.NewSmokeParticle = function(x, y) local radius = math.random(3, 5) local it = { x = x + 3 + math.random(-1, 2), y = y + 7 - radius // 2, radius = radius, color = {10, 11, 0}, id = 3.9, speed = math.random(1, 3) / 10, lessRad = math.random(1, 5) / 50, swapID = math.random(1, 3) / 60, } function it:update(i) self.y = self.y - self.speed self.radius = self.radius - ((self.radius <= 2) and self.lessRad * 2 or self.lessRad) if self.radius <= 0 then table.remove(Effects.front, i) end if math.floor(self.id - self.swapID) >= 1 then self.id = self.id - self.swapID end end function it:draw() circ(self.x, self.y, self.radius, self.color[math.floor(self.id)]) end table.insert(Effects.front, it) end _G.Portal = function(isPlayer, spawn) if isPlayer then if Player.stop then return end else PortalCool = (PortalCool > 0) and PortalCool - 1 or 0 if #Enemies.default < 4 or PortalCool > 0 then return end if math.random(6000 - 60 * (#Enemies - 4)) > #Enemies.default - 3 then return end PortalCool = math.random(600, 1200) - 60 * (#Enemies.default - 4) end local it = { x = 0, y = 0, id = 0, add = 0.1, spt = 477, spawn = spawn, } -- set target if isPlayer then it.x = Player.x it.y = Player.y it.spt = 493 it.target = Player else local fail = 0 repeat it.targetId = math.random(#Enemies.default) it.target = Enemies.default[it.targetId] fail = fail + 1 if fail == 10 then return end until (it.target.removing == 1) -- -1: in remove process -- 0: selected to remotion -- 1: valid to remove it.target.removing = 0 table.insert(PortalBuffer, it.targetId) end function it:update(i) if not self.target.stop and not self.spawn then self.x = self.target.x self.y = self.target.y end self.id = self.id + self.add if self.id >= 2.9 then self.add = -self.add -- only player if self.spawn then Player.hide = false return end self.target.stop = true if self.target == Player then Player.x = -8 Player.y = -8 Timer.on = false else self.target.removing = -1 end end if self.id <= 0.0 then if self.target == Player and not self.spawn then Trans:start(2) end table.remove(Effects.front, i) end end function it:draw() spr(self.spt + math.floor(self.id), self.x, self.y, 0) end table.insert(Effects.front, it) end --========================================================-- --====================== OBJECTS =========================-- --========================================================-- -- LIST (a-z): -- Achiev -> related achievements progress -- Coins -> coins stored and collected per player -- Coup -> values and behaviors of the player powers -- Cursor -> xy are valid positions, already XY are current positions -- Joystick -> informations about player controls -- Language -> text present in achievements and configurations -- Map -> informations to raffer a map and adjust entities positions -- Panel -> all panels present in main menu/scene -- Player -> object controled by user -- Sash -> message that will be shown in Game Over screen, after matches -- Shine -> shine of the "prize" present Game Over screen -- Stars -> effects of the menus -- Timer -> match timer -- Trans -> scene transitions -- Welcome -> basic guide for the first time playing _G.Achiev = { [1] = 0, -- progress [3] = false, [5] = 0, [6] = 0, panel = {}, } function Achiev:up(id, cond) self[id] = (cond) and self[id] + 1 or 0 end function Achiev:progress(id) if lbit.getBool(id, "achiev1") then return end if id == 1 then self:up(1, (self[1] < 10)) return end if id == 3 then Achiev[3] = not lbit.getBool(3, "achiev1") return end if id == 5 then self:up(5, (Player.hp == 1 and self[5] < 5400)) return end if id == 6 then self:up(6, true) return end end function Achiev:check() local all = lbit.getAll local conds = { self[1] == 10, tonumber(all("consumables")) >= 100, self[3], tonumber(all("sequences")) >= 5, self[5] == 5400, self[6] == 50 and tonumber(all("defeats")) == 49, } local achClass, skiClass = "", "" for i = 1, 6 do if conds[i] then achClass = "achiev"..((i < 10) and "1" or "2") skiClass = "skins" ..((i + 2 < 10) and "1" or "2") if not lbit.getBool(i, achClass) then lbit.setNum(1, i, achClass) lbit.setNum(1, i + 2, skiClass) local id = 1 + (i - 1) * 2 local txt = Language.achiev[id] local sectxt = (i % 3 == 0) and Language.secAch[id] or nil self:newPanel(txt, sectxt) if not LuckyBox then LuckyBox = true lbit.setNum(1, 1, "skins1") end end end end end function Achiev:newPanel(txt, sectxt) local it = { x = 157, y = 139, tx = 192, -- text position Y = 139, -- base of Y timer = 300, glitch = -1.0, special = (sectxt ~= nil), txt = txt, sectxt = sectxt, } function it:update() -- move to bottom if self.timer == 0 then self.y = self.y + 0.25 if self.y == self.Y then table.remove(Achiev.panel, 1) end return end -- move to top if self.Y - math.floor(self.y) < 12 then self.y = self.y - 0.25 return end -- countdown in top zenith self.timer = self.timer - 1 -- glitch effect if not self.special or self.timer > 200 then return end self.glitch = (self.glitch < 1.0) and self.glitch + 1 or -1.0 self.tx = 192 + math.floor(self.glitch) -- set "true" text if self.timer < 180 then self.special = false self.txt = self.sectxt self.tx = 192 end end function it:draw() rect( self.x - 1, self.y - 1, 84, 10, 0) -- outline rect( self.x, self.y, 83, 9, 11) -- bg rectb(self.x, self.y, 83, 9, 09) -- border rectb(self.x, self.y, 9, 9, 08) -- icon border spr(197, self.x, self.y, 0) -- icon plus.pCenter(self.txt, self.tx + 10, self.y + 4, 12, false, 1, true) end Sound(7) table.insert(self.panel, it) end function Achiev:panelUpdate() if self.panel[1] == nil then return end self.panel[1]:update() end function Achiev:panelDraw() if self.panel[1] == nil then return end self.panel[1]:draw() end _G.Coins = {} function Coins:save() if self.y > 130.1 and self.tic == 90 and self.inMatch > 0 then self.y = 130.0 end self.inMem = self.inMem + self.inMatch self.inMatch = 0 lbit.setAll(self.inMem, "coins", false, true) end function Coins:boot() self.y = 140.0 self.tic = 90 -- to update values, in 'game over' self.timer = 300 -- cooldown to hide it, after save all coins self.inMem = math.floor(lbit.getAll("coins")) -- coins stored in persistent memory self.inMatch = 0 -- obtained in the last match self.sndCool = 0 -- cooldown to play it sound effect, if many coins are collected end function Coins:update() if Sash.on then return end if self.inMatch == 0 and self.y < 140.0 and self.timer > 0 then self.timer = self.timer - 1 return end if (self.y > 130.1 and self.inMatch > 0) or (self.y < 140.0 and self.inMatch == 0) then self.y = self.y + ((self.inMatch == 0) and 0.2 or -0.2) return end if self.inMatch == 0 then return end if self.tic > 0 then self.tic = self.tic - 1 return end self.sndCool = (self.sndCool > 0) and self.sndCool - 1 or 0 if self.inMatch <= 25 or self.sndCool == 0 then Sound(5) self.sndCool = 3 end self.tic = (self.inMatch <= 5) and 30 or (self.inMatch <= 10) and 10 or (self.inMatch <= 25) and 3 or (self.inMatch <= 50) and 1 or 0 self.inMatch = self.inMatch - 1 self.inMem = self.inMem + 1 end function Coins:draw() local txt = "Coins: "..self.inMem if self.inMatch > 0 then txt = txt.." (+"..self.inMatch..")" end plus.pBorder(txt, 120, math.floor(self.y), 5, 0, false, 1, nil, false, true) end _G.Coup = {} function Coup:boot() self.x = lbit.getNum(1, "attack", 3) self.y = lbit.getNum(4, "attack", 3) self.radius = lbit.getNum(7, "attack", 2) self.opacity = lbit.getNum(9, "attack") end function Coup:update(click) end function Coup:draw() end _G.Cursor = {x = -5, y = -5, X = 0, Y = 0, spt = 470, clicking = false, click = 0, clicking2 = false} function Cursor:update() -- positions self.X, self.Y, self.clicking, _, self.clicking2 = mouse() if coli.touch(-8, -4, 247, 139) then self.x, self.y = self.X, self.Y end -- click system if self.click == 1 then if not self.clicking then self.click = 0 end return false end -- do not click during the transition if not Trans.on and self.clicking and self.click == 0 then self.click = 1 return true end return false end function Cursor:draw() if not coli.touch(0, 0, 239, 135) then return end poke(0x03FFB, 0) if lbit.getBool(9, "config1") then spr(470 + self.click, self.x - 3, self.y - 3, 4) end end _G.Joystick = {ctrl = {}} function Joystick:setPos(cond, origin, class, hold) if cond then origin.x, origin.y = Cursor.x, Cursor.y lbit.setNum(origin.x, 1, class, 3) lbit.setNum(origin.y, 4, class, 3) self["hold"..hold] = true end end function Joystick:confSave(tag, cond, memID, class, len, max, min, invert) if cond then NextSubMem(memID, class, len, max, min, invert) self.ctrl[tag] = lbit.getNum(memID, class, len) end end function Joystick:confMenuUpdate(click) local ctrl = self.ctrl if self.info then if click then self.info = false end return end -- move control local largeRad = (ctrl.radius > ctrl.bradius) and ctrl.radius or ctrl.bradius local jCircle = coli.circle(Cursor, {ctrl.x, ctrl.y, largeRad}) or self.holdJoy local aCircle = coli.circle(Cursor, Coup) or self.holdAtk if Cursor.clicking and ((ctrl.mode == 3 and jCircle) or (self.confID == 4 and aCircle)) then local isJoy = ctrl.confID ~= 4 and jCircle local isAtk = self.confID == 4 and aCircle if isJoy or isAtk then if not coli.touch(self.panelX, self.panelY, self.panelX + 80, self.panelY + 41) then if coli.touch(0, 0, 239, 135) then self:setPos(self.confID ~= 4, ctrl, "joystick1", "Joy") self:setPos(self.confID == 4, Coup, "attack" , "Atk") end end end else self.holdJoy = false self.holdAtk = false end if not click then return end local X, Y = self.panelX, self.panelY local x, y = 0, 0 -- "title" x, y = X + 3, Y + 3 if coli.touch(x, y, x + 73, y + 8) then --4 Sound(1) self.confID = NextNum(self.confID, 1, 3, Cursor.x < 120) end for i = 0, 1 do -- option 1/2 x, y = X + 3, Y + 14 + 9 * i if coli.touch(x, y, x + 73, y + 6) then Sound(1) -- general if self.confID == 1 then self:confSave("show", i == 0, 4, "joystick2", 1, 3) self:confSave("mode", i == 1, 5, "joystick2", 1, 3) -- button elseif self.confID == 2 then self:confSave("radius", i == 0, 7, "joystick1", 2, 50, 5, Cursor.x < 120) self:confSave("opacity", i == 1, 9, "joystick1", 1, 3) -- border elseif self.confID == 3 then self:confSave("bradius", i == 0, 1, "joystick2", 2, 50, 5, Cursor.x < 120) self:confSave("bopacity", i == 1, 3, "joystick2", 1, 3) -- attack button else if i == 0 then NextSubMem(7, "attack", 2, 50, 5, Cursor.x < 120) Coup.radius = lbit.getNum(7, "attack", 2) else NextSubMem(9, "attack", 1, 3) Coup.opacity = lbit.getNum(9, "attack") end end Language:backup("joys", true) Language:update("joys") end -- exit/save x, y = X + 4 + 65 * i, Y + 32 if coli.touch(x, y, x + 6, y + 6) then Sound(1) if i == 0 then self.config = false else self.info = true end break end end -- packages x, y = X + 14, Y + 32 if coli.touch(x, y, x + 51, y + 6) then Sound(1) Joystick.confPk = not Joystick.confPk end end function Joystick:confMenuDraw() -- joystick local ctrl = self.ctrl if ctrl.mode ~= 1 and self.confID ~= 4 then local largeRad = (ctrl.radius > ctrl.bradius) and ctrl.radius or ctrl.bradius for i = 2, 0, -2 do circ(ctrl.x, ctrl.y, largeRad + 2 + i, ((i == 0) and ((ctrl.mode == 3) and 6 or 2) or 0)) end -- arrows (mode: fixed) if ctrl.mode == 3 then local arrow = { [0]={ 7, 0, 2, -4, 2, 5}, -- right { 0, 7, -4, 2, 5, 2}, -- below {-5, 0, 0, 5, 0, -5}, -- left { 0, -5, 5, 0, -5, 0}, -- top } local extraRad = 25 + largeRad // 2 local x, y, r = 0, 0, 0 for i = 0, 3 do r = math.rad(90 * i) x = math.floor(ctrl.x + math.cos(r) * extraRad) y = math.floor(ctrl.y + math.sin(r) * extraRad) tri( x + arrow[i][1], y + arrow[i][2], x + arrow[i][3], y + arrow[i][4], x + arrow[i][5], y + arrow[i][6], 7) end end ctrl:draw(true) end -- attack button local atkMode = lbit.getNum(7, "config1") if self.confID == 4 then for i = 2, 0, -2 do circ(Coup.x, Coup.y, Coup.radius + 2 + i, ((i == 0) and ((atkMode == 3) and 6 or 2) or 0)) end if Coup.opacity == 2 then circ( Coup.x, Coup.y, Coup.radius, 4) end -- front if Coup.opacity ~= 3 then circb(Coup.x, Coup.y, Coup.radius, 7) end -- line local scale = Coup.radius // 10 + 1 local adjPos = 4 * scale spr(454, Coup.x - adjPos, Coup.y - adjPos, 13, scale) end local x, y = self.panelX, self.panelY local color = 7 if (ctrl.mode == 1 and self.confID ~= 4) or (self.confID == 4 and atkMode ~= 3) then color = 11 end -- background rect( x, y, 80, 42, 11) rectb(x, y, 80, 42, 8) -- title rect(x + 3, y + 3, 74, 9, 10) plus.pCenter(TextAnim(Language.joys[1 + 3 * (self.confID - 1)], true), x + 40, y + 7, 7, false, 1, false, true) for i = 0, 1 do -- options rect(x + 3, y + 14 + 9 * i, 74, 7, 10) plus.pCenter(Language.joys[2 + i + 3 * (self.confID - 1)], x + 40, y + 17 + 9 * i, ((i == 1 and self.confID == 1) and 7 or color), false, 1, true) -- close/informations spr(10 + i, x + 4 + 65 * i, y + 32, 15) end -- packages rect(x + 14, y + 32, 52, 7, 10) plus.pCenter(Language.options[19], x + 39, y + 35, 7, false, 1, true) -- informations if self.info then Trans:shadow() rect( 45, 44, 154, 53, 11) rectb(45, 44, 154, 53, 8) -- attack button | joystick local id = (self.confID == 4) and 2 or 1 plus.pList(Language.info[id], 120, 68, 7, 7, false, 1, true, 0, true) end end function Joystick:confPackUpdate(click) if not click then return end local x, y = self.panelX + 3, self.panelY + 42 if not coli.touch(x, self.panelY + 39, x + 79, self.panelY + 60) then self.confPk = false return end for i = 0, 1 do if coli.touch(x, y + 9 * i, x + 73, (y + 9 * i) + 6) then Sound(1) if i == 0 then ConfigPacks() -- computer else ConfigPacks(true) -- phone end self.confID = 1 self.confPk = false break end end end function Joystick:confPackDraw() local x, y = self.panelX, self.panelY + 32 rect( x, y + 7, 80, 22, 11) rectb(x, y + 7, 80, 22, 8) for i = 0, 1 do rect(x + 3, y + 10 + 9 * i, 74, 7, 10) plus.pCenter(Language.options[20 + i], x + 39, y + 13 + 9 * i, 7, false, 1, true) end end function Joystick:boot() self.confID = 1 self.config = false self.confPk = false -- packages to configuration self.info = false -- informations about joystick and attack button self.tClose = 0 -- cooldown to turn on interation after close self.panelX = 80 self.panelY = 47 self.holdJoy = false self.holdAtk = false self.ctrl.tags = {"x", "y", "radius", "opacity", "show", "mode"} local b = "" for i = 1, #self.ctrl.tags - 2 do self.ctrl[self.ctrl.tags[i]] = 0 -- button b = "b"..self.ctrl.tags[i] self.ctrl[b] = 0 table.insert(self.ctrl.tags, b) end self.ctrl:boot(true) end function Joystick.ctrl:boot(memory) if memory then -- get joytick values from memory local mainMem = {{1, 4, 7, 9}, {3, 3, 2, 1}} -- index - length for i = 1, 4 do self[self.tags[i]] = lbit.getNum(mainMem[1][i], "joystick1", mainMem[2][i]) end self.bradius = lbit.getNum(1, "joystick2", 2) self.bopacity = lbit.getNum(3, "joystick2") self.show = lbit.getNum(4, "joystick2") self.mode = lbit.getNum(5, "joystick2") end -- for matches self.borderX = self.x self.borderY = self.y self.btnX = self.x self.btnY = self.y self.fixed = false end function Joystick.ctrl:update(click) if self.mode == 2 and not Cursor.clicking and self.fixed then self.fixed = false end if not Cursor.clicking or Player.stop or Player.hide then if self.mode ~= 1 then self.btnX = self.borderX self.btnY = self.borderY end return end local orgX, orgY = Cursor.x, Cursor.y local tarX, tarY = self.x, self.y if self.mode == 1 then tarX, tarY = Player.x + 3, Player.y + 3 -- very clossness if coli.circle(Cursor, {tarX, tarY, 2}) then return end elseif self.mode == 2 then -- change the joystick (center) position if not self.fixed then self.fixed = true self.borderX = Cursor.x self.borderY = Cursor.y end tarX, tarY = self.borderX, self.borderY end if self.mode ~= 1 then -- inside joystick area if coli.circle(Cursor, {self.borderX, self.borderY, self.bradius}) then self.btnX = Cursor.x self.btnY = Cursor.y else local angle = math.atan2(Cursor.y - self.borderY, Cursor.x - self.borderX) self.btnX = self.borderX + self.bradius * math.cos(angle) self.btnY = self.borderY + self.bradius * math.sin(angle) end end local angle = math.atan2(orgY - tarY, orgX - tarX) local sx, sy = Player.speed * math.cos(angle), Player.speed * math.sin(angle) if coli.box360(sx, nil, Player, 0, Map.x, Map.y) then Player.x = Player.x + sx end if coli.box360(nil, sy, Player, 0, Map.x, Map.y) then Player.y = Player.y + sy end end function Joystick.ctrl:draw(conf) local bx, by = self.x, self.y local x, y = self.x, self.y if not conf then bx = self.borderX by = self.borderY x = self.btnX y = self.btnY if Player.hide or Player.stop or self.mode == 1 or (self.show == 1 and not Cursor.clicking) or self.show == 3 then return end end -- 1: line | 2: full | 3: zero if self.bopacity == 2 then circ( bx, by, self.bradius, 11) end if self.bopacity ~= 3 then circb(bx, by, self.bradius, 7) end if self.opacity == 2 then circ( x, y, self.radius, 9) end if self.opacity ~= 3 then circb(x, y, self.radius, 8) end end _G.Language = { id = 0, bconf = {}, bstates = {}, bjoys = {}, -- language index | backup mapTheme = nil, mapName = nil, conf = nil, states = nil, info = nil, joys = nil, options = nil, achiev = nil, sectAch = nil, } function Language:cat(id, tag, str) self[tag][id] = self[tag][id]..str end function Language:backup(tag, get) -- [b]ackup for i = 1, #self[tag] do if get then self[tag][i] = self["b"..tag][i] else self["b"..tag][i] = self[tag][i] end end end function Language:update(Type) local o, opt = self.options, nil -- sub-memory index | index to text content | class number tag local id, optID, tagn = 0, nil, "" if Type == "stat" then local class = {"coins", "victories", "defeats", "sequences"} for i = 1, 4 do self.states[i] = self.states[i]..lbit.getAll(class[i]) end return end -- joystick and attack if Type == "joys" then opt = { {o[14], o[13], o[15]}, -- when touched, always, never {o[12], o[11], o[10]}, -- follow cursor, touch and fix, fixed {o[17], o[18], o[16]}, -- line, full, zero } -- joystick self:cat(2, "joys", opt[1][ lbit.getNum(4, "joystick2") ]) -- show self:cat(3, "joys", opt[2][ lbit.getNum(5, "joystick2") ]) -- mode self:cat(6, "joys", opt[3][ lbit.getNum(9, "joystick1") ]) -- opacity self:cat(9, "joys", opt[3][ lbit.getNum(3, "joystick2") ]) -- bopacity local function radius(id, memID, tag) tag = ((tonumber(tag)) and "joystick"..tag or tag) value = lbit.getNum(memID, tag, 2) value = (value < 10) and "0"..value or value Language.joys[id] = "<< "..Language.joys[id].." ("..value..") >>"-- radius end radius(5, 7, "1") -- radius radius(8, 1, "2") -- bradius -- attack radius(11, 7, "attack") self:cat(12, "joys", opt[3][ lbit.getNum(9, "attack") ]) return end -- ("conf") configurations opt = { {o[8], o[10], o[9]}, -- follow, fixed, both {o[5], o[6], o[ 7]}, -- mouse 2, auto, button {"EN", "PT"}, {o[1], o[2]}, -- on, off {o[3], o[4]}, -- normal, big } for i = 1, 6 do optID = i - 1 if i == 1 then optID = i end id, tagn = i + 4, "1" if i == 6 then id, tagn = 1, "2" end -- get a value from a sub-memory and concat it with text self:cat(i, "conf", opt[optID][ lbit.getNum(id, "config"..tagn) ]) end end function Language:updateAll() local toLangBk = {"conf", "states", "joys"} if lbit.getBool(8, "config1") then Language:en() else Language:pt() end for i = 1, 3 do Language:backup(toLangBk[i]) end Language:update("stat") Language:update("conf") Language:update("joys") end function Language:en() local l, n = self, "\n" l.mapTheme = {[0] = "Random", "Industries CRC", "#2", "#3", "#4"} l.mapName = {[0] = "Random", "Ambush", "Cyber-Parade", "Under Pressure"} l.conf = {"Lifebar: ", "XP bar: ", "Attack: ", "Idioma: ", "Cursor: ", "Button: ", "Touch controls"} l.states = {[0] = "Status: ", "Coins: ", "Victories: ", "Defeats: ", "Sequence: "} l.info = { { -- joystick "Regardless of the control mode selected,", "it is not possible to move the player", "case the cursor/touch is outside of the", "screen. The red borders indicate this.", "The Joystick is recommended to phone", "users.", }, { -- attack button "After load the Power Bar, click above", "the Attack Button and, then, click in", "any place on screen.", "Some Powers will be direct it until the", "point clicked.", "Click again on Attack Button to cancel", "the attack.", }, } l.joys = { "JOYSTICK", "Show: ", "Mode: ", "J. BUTTON", "Scale", "Opacity: ", "J. BORDER", "Scale", "Opacity: ", "ATK BUTTON", "Scale", "Opacity: ", -- attack } l.options = { "ON", "OFF", "Normal", "Big", "Mouse2", "Auto", "Button", "Follow", "Both", -- "Fixed" (hp | xp) "Fixed", "Touch and fix", "Follow cursor", "Always", "When touched", "Never", "Zero", "Line", "Full", "Con. packages", "Computer", "Phone" } l.achiev = { -- "TTTTTTTTTTTTTTTTTT" "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" "Balance", "Lose and recover 10" ..n.. "Charges in a match.", "Full", "Collect over 100" ..n.. "Consumables.", '"Chamaleon"', '"The white of your eyes' ..n.. 'remind me of something."', "Legendary", "Win 5 matches in a row.", "Impossible", "Spend more that 1:30" ..n.. "minutes on just one" ..n.. "Charge.", '"Empty"', '"I hope this was all on' ..n.. 'purpose."', } l.secAch = { -- "TTTTTTTTTTTTTTTTTT" "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" [05] = "Wronged", [06] = "Find and rescue the" ..n.. "crew member.", [11] = "Just improve", [12] = "Feel the bitter taste" ..n.. "of defeat 50 times.", } end function Language:pt() local l, n = self, "\n" l.mapTheme = {[0] = "Aleatorio", "Industrias RFC", "#2", "#3", "#4"} l.mapName = {[0] = "Aleatorio", "Emboscada", "Ciber-Defile", "Sob Pressao"} l.conf = {"B.d vida: ", "B.d XP: ", "Ataque: ", "Language: ", "Cursor: ", "Botoes: ", "Contr. de toque"} l.states = {[0] = "Status", "Moedas: ", "Vitorias: ", "Derrotas: ", "Sequencia: "} l.info = { { -- joystick "Independentemente do modo de controle", "selecionado, nao eh possivel mover o", "jogador caso o cursor/toque esteja fora", "da tela. As bordas vermelhas indicam", "isso.", "O Joystick eh recomendado para usuarios", "de celular.", }, { -- attack button "Apos carregar a Barra de Poder, clique", "sobre o Botao de Ataque e, em seguida,", "clique em qualquer lugar na tela.", "Alguns Poderes irao se direcionar ate", "o ponto clicado.", "Clique novamente no Botao de Ataque para", "cancelar o ataque.", }, } l.joys = { "JOYSTICK", "Mostrar: ", "Modo: ", "J. BOTAO", "Escala", "Opacidade: ", "J. BORDA", "Escala", "Opacidade: ", "BOTAO ATK", "Escala", "Opacidade: ", -- attack } l.options = { "ON", "OFF", "Normal", "Grande", "Mouse2", "Auto", "Botao", "Seguir", "Ambos", -- "Fixo" (hp | xp) "Fixo", "Tocar e ficar", "Seguir jogad.", "Sempre", "Qu. tocado", "Nunca", "Zero", "Linha", "Cheio", "Pacot. d Conf", "Computador", "Celular" } l.achiev = { -- "TTTTTTTTTTTTTTTTTT" "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" "Equilibrio", "Perca e recupere 10" ..n.. "Cargas em uma partida", "Cheio", "Colete mais de 100" ..n.. "Consumiveis.", '"Camaleao"', '"O branco dos seus' ..n.. 'me lembra algo."', "Lendario", "Venca 5 partidas" ..n.. "seguidas.", "Impossivel", "Passe mais de 1:30" ..n.. "minutos com apenas uma" ..n.. "Carga.", '"Zerado"', '"Espero que tudo isso' ..n.. 'tenha sido proposital."', } l.secAch = { -- "TTTTTTTTTTTTTTTTTT" "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" \n "TTTTTTTTTTTTTTTTTTTTTTT" [05] = "Injusticado", [06] = "Encontre e resgate o" ..n.. "tripulante perdido.", [11] = "Apenas melhore", [12] = "Sinta o gosto amargo da" ..n.. "derrota 50 vezes.", } end _G.Map = { x = 0, y = 0, -- to adjust entity collision (with tiles) redoMarks = {}, -- redo mark tiles to spawn traps and bosses consumables = {}, -- position of consumables not collected consumable curMap = 0, curTheme = 0, -- auto defined, based in 'cur' (used to random) idMap = 0, idTheme = 0, -- defined by users lastMap = 0, lastTheme = 0, -- last map played } function Map:random(tag) local cur = "cur"..tag local last = "last"..tag repeat self[cur] = math.random(3) until (self[cur] ~= self[last]) self[last] = self[cur] end function Map:swapTile(x, y, tile, blocked, newTile) local floor = {19, 1, 1, 1} local isBlocked = (blocked) and 1 or 0 local new = newTile or (floor[self.curTheme] + isBlocked) mset(self.x + x, self.y + y, new) table.insert(self.redoMarks, { x = self.x + x, y = self.y + y, id = tile }) end function Map:redo() local cur = nil for i = #self.redoMarks, 1, -1 do cur = self.redoMarks[i] mset(cur.x, cur.y, cur.id) table.remove(self.redoMarks, i) end end function Map:add(x, y, tile, isTrap) local org, id = nil, 0 local adj = 16 * (self.curTheme - 1) if isTrap then org = Traps id = 3 -- back trap by default if tile <= 22 + adj then id = 1 elseif tile <= 24 + adj then id = 2 end else org = Doors id = 2 if tile <= 81 then id = 1 end end local new = org[org.tags[self.curTheme][id]](x, y) -- destine local dst = org.front if new.back then dst = org.back end table.insert(dst, new) end function Map:saveAndSet() local tile = 0 for y = 0, 16 do for x = 0, 29 do -- traps local tile = mget(Map.x + x, Map.y + y) if self.curTheme == 1 and tile >= 21 and tile <= 29 and tile ~= 25 then if tile ~= 22 then self:add(x, y, tile, true) else self:swapTile(x, y, tile) -- crusher yellow tile end end -- doors, bosses, ... if self.curTheme == 1 and tile >= 80 and tile <= 84 then if tile ~= 82 and tile ~= 84 then self:add(x, y, tile) else self:swapTile(x, y, tile, (tile == 82)) -- player/boss spawn end end end end end function Map:boot(toMatch) if not toMatch then self.curMap = lbit.getNum(3, "config2") self.idMap = self.curMap self.curTheme = lbit.getNum(4, "config2") self.idTheme = self.curTheme return end self:redo() self.curTheme = self.idTheme self.curMap = self.idMap if self.curTheme == 0 then self:random("Theme") end if self.curMap == 0 then self:random("Map" ) end Map.x = 30 * (Map.curMap - 1) Map.y = 17 * (Map.curTheme - 1) -- player start position local pPos = {{116, 68}, {172, 76}, {116, 100}} Player.x = pPos[self.curMap][1] Player.y = pPos[self.curMap][2] self:saveAndSet() end _G.Panel = { -- left, right show = {0, 0}, -- show mode of icons: -1: hidding 0: shown | 1: showing onlineX = {0, 0}, -- to hide icons of panel are not openned online = {nil, nil}, -- the panel opened tags = {"stat", "achi", "conf", "best", "skin", "mapp"} } Panel.stat = {x = -60, openState = 0, minX = -60, maxX = 0} function Panel.stat:update(click) end -- void function Panel.stat:draw() -- background local width = math.abs(self.maxX - self.minX) + 1 rect( self.x - 1, 24, width, 39, 11) rectb(self.x - 1, 24, width, 39, 8) -- content local color = {5, 8, 1, 7} plus.pShadow(Language.states[0], 13, self.x + 1, 26, 12) for i = 1, #Language.states do print(Language.states[i], self.x + 1, 34 + 7 * (i - 1), color[i], false, 1, true) end end Panel.achi = {x = -100, openState = 0, minX = -100, maxX = 0, id = 0} function Panel.achi:update(click) if not click then return end for i = 4, 121, 117 do if coli.touch(self.x + 35, i, self.x + 66, i + 7) then Sound(1) self.id = NextNum(self.id, 0, 1, i == 4) if i == 4 then BtnTop = 15.9 else BtnBottom = 31.9 end break end end end function Panel.achi:draw() -- panel bg rect(self.x, 0, math.abs(self.maxX - self.minX), 136, 11) line(self.x + -self.minX - 1, 0, self.x + -self.minX - 1, 135, 8) -- achievement bg local color = 0 for i = 0, 2 do color = (lbit.getBool(i + 1 + self.id * 3, "achiev1")) and 6 or 2 rect( self.x + 3, 18 + 34 * i, 93, 31, color) rectb(self.x + 3, 18 + 34 * i, 93, 31, color - 1) end -- achievement text local id, memID, font = 0, 0, nil for i = 1, 5, 2 do id = i + self.id * 6 memID = i // 2 + 1 + self.id * 3 font = (i < 5 or not lbit.getBool(memID, "achiev1")) and Language.achiev or Language.secAch plus.pShadow(font[id], 13, self.x + 5, 21 + 17 * (i - 1), 12) print(font[id + 1], self.x + 5, 29 + 17 * (i - 1), 7, false, 1, true) end -- buttons local spt = {BtnTop, BtnBottom} for i = 0, 1 do for j = 0, 1 do spr(math.floor(spt[i + 1]), self.x + 35 + 16 * j, 4 + 117 * i, 0, 2, j) end end end Panel.conf = {x = -100, openState = 0, minX = -100, maxX = 0} function Panel.conf:update(click) if not click then return end local x, y = 0, 0 local upLang, big = false, lbit.getBool(1, "config2", 2) for i = 0, 8 do for j = 0, 1 do x = self.x + 2 + 81 * j y = 10 + 14 * i if coli.touch(x, y, x + 11, y + 11) or (big and i > 1 and coli.touch(x + 12, y + 2, x + 96, y + 9)) then if i == 0 then NextSubMem(1, "config1", 2, 15, 0, (j == 0)) Volume[1] = lbit.getNum(1, "config1", 2) -- songs elseif i == 1 then NextSubMem(3, "config1", 2, 15, 0, (j == 0)) Volume[2] = lbit.getNum(3, "config1", 2) -- sound effects elseif i == 2 then NextSubMem(5, "config1", 1, 3) Player.hpMode = lbit.getNum(5, "config1") -- lifebar --elseif i == 3 then NextSubMem(6, "config1", 1, 3) Player.xpMode = lbit.getNum(6, "config1") -- xpbar --elseif i == 4 then NextSubMem(7, "config1", 1, 3) Player.atkMode = lbit.getNum(7, "config1") -- attack elseif i == 5 then NextSubMem(8, "config1", 1, 2) upLang = true-- language elseif i == 6 then NextSubMem(9, "config1", 1, 2) -- cursor elseif i == 7 then NextSubMem(1, "config2", 1, 2) BigBtnTim, BigBtnMode = 2, not BigBtnMode -- button elseif i == 8 then Joystick.config = not Joystick.configl Joystick.ctrl.x, Joystick.ctrl.y = lbit.getNum(1, "joystick1", 3), lbit.getNum(4, "joystick1", 3) Coup.x, Coup.y = lbit.getNum(1, "attack", 3), lbit.getNum(4, "attack", 3) end Sound(1) -- after "sound effect" to be defined if upLang then if lbit.getNum(8, "config1") == 1 then Language:en() else Language:pt() end local ltags = {{"conf", "states", "joys"}, {"conf", "stat", "joys"}} for i = 1, 3 do Language:backup(ltags[1][i]) Language:backup(ltags[1][i], true) Language:update(ltags[2][i]) end end Language:backup("conf", true) Language:update("conf") return end if i > 1 then break end end end end function Panel.conf:draw() local Y = 10 -- panel bg local width = math.abs(self.maxX - self.minX) rect( self.x, Y - 3, width, 129, 11) rectb(self.x - 1, Y - 3, width + 1, 130, 8) -- icons for i = 0, 8 do for j = 0, ((i < 2) and 1 or 0) do -- to music and sound for l = 0, 1 do for t = 0, 1 do circ(self.x + 7 + t + 81 * j, Y + 5 + 14 * i + l, 5, 10) end end spr(242 + i - 2 * j, self.x + 4 + 81 * j, Y + 2 + 14 * i, 11) end end -- sound bars local barX, adjY, lineX, lineY, bwidth = 0, 0, 0, 0, 0 for i = 0, 1 do barX = self.x + 19 adjY = 14 * i bwidth = lbit.getNum(1 + 2 * i, "config1", 2) * 4 rect(barX, Y + adjY, 59, 12, 10) -- horizontal border rect(barX, Y + 1 + adjY, 59, 10, 2) -- background rect(barX, Y + 1 + adjY, bwidth, 10, 5) -- volume for j = 0, 15 do lineX = self.x + 18 + 4 * j line(lineX, Y + 1 + adjY, lineX, Y + 10 + adjY, 10) end end plus.pList(Language.conf, self.x + 16, Y + 32, 7, 14) local commingSoon = (lbit.getBool(8, "config1")) and "[ Comming soon... ]" or "[ Em Breve... ]" for i = 0, 1 do rect(self.x + 16, 55 + 14 * i, 83, 7, 11) print(commingSoon, self.x + 16, 56 + 14 * i, 7, false, 1, true) end if BigBtnTim > 0.0 then for i = 0, 8 do for j = 0, 1 do rectb(self.x + 2 + 81 * j, Y + 14 * i, 12, 12, 3 - math.ceil(BigBtnTim)) if i > 1 then if BigBtnMode then rectb(self.x + 14 + 81 * j, Y + 2 + 14 * i, 85, 8, 3 - math.ceil(BigBtnTim)) end break end end end end end Panel.best = {x = 240, openState = 0, minX = 240, maxX = 180} function Panel.best:update(click) end function Panel.best:draw() -- background local width = math.abs(self.maxX - self.minX) rect( self.x, 33, width, 21, 11) rectb(self.x, 33, width + 1, 21, 8) print((lbit.getBool(8, "config1")) and "[Comming soon...]" or "[ Em Breve... ]", self.x + 2, 41, 7, false, 1, true) end Panel.skin = {x = 240, openState = 0, minX = 240, maxX = 180, id = 1, unblocked = true} function Panel.skin:update(click) Player:animSkin(true) if click then local x = 0 -- select a skin x = self.x + 10 if self.unblocked and Player.skin.id ~= self.id and coli.touch(x, 120, x + (math.abs(self.maxX - self.minX)) - 21, 130) then Sound(1) Player.skin.id = self.id Player.skin.cur = self.id lbit.setNum(self.id, 2, "config2") return end -- skin in display for i = 3, 49, 46 do x = self.x + i if coli.touch(x, 91, x + 7, 102) then Sound(1) self.id = NextNum(self.id, 0, 7, i == 3) if i == 3 then BtnLeft = 47.9 else BtnRight = 47.9 end break -- jump, for update "unblocked" end end end -- skin that is shown local memID = self.id + 1 local class = "skins1" if memID > 9 then memID = memID - 9 class = "skins2" end self.unblocked = lbit.getBool(memID, class) end function Panel.skin:draw() -- background local width = math.abs(self.maxX - self.minX) rect( self.x, 60, width, 76, 11) rectb(self.x, 60, width + 1, 77, 8) -- values local selectd = (Player.skin.id == self.id) local btnText = "Select" local nameCol = 7 if selectd then btnText = "Selected" nameCol = 12 end -- skin local names = {[0] = "Lucky Box", "New Astrun", "Spirit", "Terra Mimic", "Guy Sus", "Champion", "Knight", "Wendy"} rect(self.x + 1, 68, width - 1, 7, 10) tri(self.x + 1, 136, self.x + 17, 115, self.x + 17, 136, 10) tri(self.x + width, 136, self.x + width - 17, 115, self.x + width - 17, 136, 10) rect(self.x + 17, 114, 26, 22, 10) local curName = names[self.id] local curSprt = self.id if not self.unblocked then curName = "["..((self.id == 0) and "?" or self.id - 1).."]" curSprt = -1 end plus.pCenter(curName, self.x + 30, 71, nameCol, false, 1, true) Player:animSkin(false, curSprt, self.x + 14, 82, 4) -- Selected / Select if not selectd and self.unblocked then rect( self.x + 10, 120, width - 20, 11, 9) -- button rectb(self.x + 10, 120, width - 20, 11, 8) plus.pCenter(btnText, self.x + 29, 125, 7) end -- swap button local spt = {BtnLeft, BtnRight} for i = 0, 1 do spr(spt[i + 1], self.x + 3 + 38 * i, 91, 0, 2, i) end end Panel.mapp = {x = 240, openState = 0, minX = 240, maxX = 140} function Panel.mapp:update(click) if not click then return end local x, y, w = 0, 0, 0 --[[ theme x = self.x + 1 y = 66 w = math.abs(self.maxX - self.minX) - 2 if coli.touch(x, y, x + w, y + 7) then Map.theme = NextNum(Map.theme, 0, 2, Cursor.x < x + w // 2) lbit.setNum(Map.theme, 4, "config2") return end ]] -- map for i = 0, 3 do x = self.x + 10 y = 82 + 12 * i if coli.touch(x, y, x + 80, y + 8) then if Map.idMap ~= i then Sound(1) end Map.idMap = i lbit.setNum(i, 3, "config2") return end end end function Panel.mapp:draw() -- background local width = math.abs(self.maxX - self.minX) rect( self.x, 58, width, 79, 11) rectb(self.x, 58, width + 1, 79, 8) -- theme --plus.pShadow(TextAnim(Language.mapTheme[Map.curTheme], true), 13, self.x + 50, 69, 12, false, 1, false, true) plus.pShadow(Language.mapTheme[Map.curTheme], 13, self.x + 50, 69, 12, false, 1, false, true) -- maps for i = 0, 3 do rect(self.x + 10, 82 + 12 * i, 81, 9, 10)-- bg if Map.idMap == i then rectb(self.x + 10, 82 + 12 * i, 81, 9, 5) end -- selected plus.pCenter(Language.mapName[i], self.x + 50, 86 + 12 * i, 7, false, 1, true) end end function Panel:hsIcon(mainCond, valueCond1, valueCond2, i, spd1, spd2) if mainCond then if self.show[i] == -1 and valueCond1 then self.onlineX[i] = self.onlineX[i] + spd1 -- close elseif self.show[i] == 1 and valueCond2 then self.onlineX[i] = self.onlineX[i] + spd2 -- open end if self.show[i] ~= 0 and (self.onlineX[i] == 0 or math.abs(self.onlineX[i]) == 6) then self.show[i] = 0 if self.onlineX[i] == 0 then self.online[i] = nil end end end end function Panel:update(click) -- LOAD A PANEL OPENNED local cur, online = nil, nil -- current panel local spd = 0 -- load panel content for i = 1, 2 do -- Hide/Show Icon Panel:hsIcon(i == 1, self.onlineX[i] < 0, self.onlineX[i] > -6, i, 1, -1) -- left Panel:hsIcon(i == 2, self.onlineX[i] > 0, self.onlineX[i] < 6, i, -1, 1) -- right online = self.online[i] if online ~= nil and self.show[i] == 0 then if online.x == online.maxX then online:update(click) end -- openned/closing if online.openState ~= 0 then spd = ((i == 1) and 10 or -10) -- invert based in side -- clossing if self.onlineX[i] ~= 0 and online.openState == -1 and online.x ~= online.minX then online.x = online.x - spd -- turn off panel if online.x == online.minX then self.show[i] = -1 online.openState = 0 end -- openning elseif math.abs(self.onlineX[i]) == 6 and online.openState == 1 and online.x ~= online.maxX then online.x = online.x + spd end end end end -- OPEN A PANEL if not click then return end local id = 1 local X, Y = 0, 0 -- base of postions local x1, y1, x2, y2 = 0, 0, 0, 0 local ooIcon = nil -- on/off icons local noOrIt = nil -- none opened or it is openned local oState = nil -- .openState for i = 0, 1 do -- load buttons for j = 0, 2 do cur = self[self.tags[id]] id = id + 1 -- open/closed | open/closing ooIcon = (self.onlineX[i + 1] == 0 or math.abs(self.onlineX[i + 1]) == 6) noOrIt = (self.online[i + 1] == nil or self.online[i + 1] == cur) oState = (cur.openState ~= -1 or (cur.openState == 1 and cur.x == cur.maxX)) if ooIcon and noOrIt and oState then -- hitbox positions X = cur.x + -cur.minX - 1 + 235 * i Y = 40 + 30 * j x1 = X + 1 - i y1 = Y x2 = x1 + 5 y2 = y1 + 6 if BigBtnMode then x1 = X + ((i == 1) and -6 or 1) y1 = Y - 7 x2 = x1 + 11 y2 = y1 + 20 end if coli.touch(x1, y1, x2, y2) then Sound(1) cur.openState = (cur.openState == 0) and 1 or -1 self.show[i + 1] = (self.onlineX[i + 1] == 0) and 1 or 0 self.online[i + 1] = cur break -- jump to other side end end end end end function Panel:draw() -- panel icons (i, j, l) local ix, iy, ly, lflip = 0, 0, 0, 0 local tx, ty, tw, th = 0, 0, 0, 0 local cur, panelID, iconSpt = nil, 1, nil -- panel icon (to open/close them) for i = 0, 1 do for j = 0, 2 do cur = self[self.tags[panelID]] panelID = panelID + 1 -- icon ix = cur.x + -cur.minX - 1 + 235 * i iy = 40 + 30 * j if self.online[i + 1] ~= cur and cur.openState == 0 then ix = ix + self.onlineX[i + 1] end iconSpt = (self.online[i + 1] ~= nil and self.online[i + 1].x == self.online[i + 1].maxX) and 10 or ((4 + 3 * i) + j) spr(iconSpt, ix, iy, 15) -- big button if BigBtnMode then for l = -1, 1, 2 do ly = iy + 7 * l - ((l == 1) and 1 or 0) lflip = ((l == -1) and 0 or 2) spr(12, ix - i, ly, 15, 1, lflip + i) end end if BigBtnTim > 0.0 then tx = ix + 1 - i ty = iy tw = 6 th = 7 if BigBtnMode then tx = tx - ((i == 1) and 3 or 0) ty = iy - 7 tw = 12 th = 21 end rectb(tx, ty, tw, th, 3 - math.ceil(BigBtnTim)) end end end -- draw openned panels for i = 1, 2 do if self.online[i] ~= nil then if self.online[i].openState ~= 0 then self.online[i]:draw() end end end end _G.Player = {} function Player:animSkin(update, id, x, y, scale) local skin = self.skin id = (id or skin.cur) - 1 scale = scale or 1 if update then skin.timer = skin.timer + 0.1 if skin.timer >= 2.0 then skin.timer = 0 end return end spr(288 + math.floor(skin.timer) + 16 * id, x, y, 11, scale) end function Player:randomSkin() if self.skin.id ~= 0 then return end local skins = {1} for i = 3, 8 do if lbit.getBool(i, "skins1") then table.insert(skins, i - 1) end end repeat self.skin.cur = skins[math.random(#skins)] until (self.skin.cur ~= self.skin.last) self.skin.last = self.skin.cur end function Player:damage() if (self.invensible > 0 and not self.hide) or self.hp == 0 then return end Sound(8) self.hp = self.hp - 1 self.invensible = 30 self.showHp = 60 if self.hp > 0 or self.loser then return end self.loser = true Portal(true) end function Player:startDebuff(obj, damage) if not self:hitbox(obj, not damage) then return end self.debuff = 60 if self.curTheme == 1 then self.speed = 0.25 return end -- shock end function Player:hitbox(itHitbox, noDamage) local body = coli.newBody("rect", math.floor(Player.x + 1), math.floor(Player.y), 6, 8) if itHitbox.radius ~= nil then local dist = coli.distance({self.x + 3, self.y + 3}, itHitbox) if dist > itHitbox.radius + 5 then return end itHitbox = coli.newBody("circ", itHitbox.x + 1, itHitbox.y + 1, itHitbox.radius) if coli.shapesMix(itHitbox, body) then if not noDamage then self:damage() end return true end return false end if coli.rectangle(body, itHitbox) then if not noDamage then self:damage() end return true end return false end function Player:ui() local x, y = 0, 0 local colors = {5, 12, 1} for i = 1, 3 do if self.hp < 4 - i then colors[i] = colors[i] + 1 else break end end -- LIFEBAR -- follow if self.hpMode ~= 2 and self.showHp > 0 then if self.x == -8 then return end x, y = self.x - 7, self.y + 10 if self.y > 100 then y = self.y - 10 end for i = 0, 2 do rect(x + 5 + 4 * i, y + 2, 4, 4, colors[i + 1]) end for i = 0, 1 do spr(485, x + 6 * i, y, 15, 1, i, 0, 2, 1) end end -- fixed if self.hpMode > 1 then x, y = 0, 0 for i = 0, 2 do if i < 2 then rect(x + 3 + 6 * i, y + 3, 4, 6, colors[1 + 2 * i]) else rect(x + 4, y + 9, 8, 4, colors[2]) end end for i = 0, 1 do spr(487 , x + 8 * i, y, 15, 1, i, 0, 1, 2) end end end function Player:boot() self.x = 0 self.y = 0 self.alignX = 0 self.alignY = 0 self.hp = 3 self.speed = 0.5 self.loser = false self.stop = false self.hide = true -- timers self.showHp = 0 self.invensible = 0 self.debuff = 0 self.visualEfct = 0 if self.skin == nil then self.hpMode = lbit.getNum(5, "config1") self.xpMode = lbit.getNum(6, "config1") self.atkMode = lbit.getNum(7, "config1") local mem = lbit.getNum(2, "config2") self.skin = { cur = mem, id = mem, last = 0, timer = 0.0, toSlct = 0, } end self:randomSkin() end function Player:update() self.alignX, self.alignY = coli.mapAlign(self, true) self.showHp = (self.showHp > 0) and self.showHp - 1 or 0 self.invensible = (self.invensible > 0) and self.invensible - 1 or 0 self.visualEfct = (self.visualEfct > 0) and self.visualEfct - 1 or 0 if self.debuff > 0 then self.debuff = self.debuff - 1 if self.visualEfct == 0 then self.visualEfct = 10 end if Map.curTheme == 1 then self.speed = 0.25 end elseif self.speed < 0.5 then self.speed = 0.5 end Traps.effects.conveyorBelt(self) Achiev:progress(5) end function Player:draw() if self.hide then return end -- sprite if (self.invensible // 5) % 2 == 0 then Player:animSkin(false, nil, self.x, self.y) end -- debuff if self.debuff > 0 and self.visualEfct > 5 then spr(460 + Map.curTheme - 1, self.x, self.y, 0) end end _G.Sash = {} function Sash:boot() self.x = -50 self.y = 137 self.sx = 0 self.sy = 0 self.on = true end function Sash:update() -- speed (slow when clossness of the center) self.sx = (self.x >= 77 and self.x <= 93) and 0.50 or math.abs(88 - self.x) / 8 self.sy = (self.y >= 60 and self.y <= 76) and 0.25 or math.abs(68 - self.y) / 8 -- moviment if self.x < 240 then self.x = self.x + self.sx end if self.y > -16 then self.y = self.y - self.sy end -- animation end if self.x >= 240 and self.y <= -16 then self.on = false end end function Sash:draw() if Menu ~= 2 or (Menu == 2 and not Sash.on and Trans.timer >= 24.0) then return end rect( 0, self.y, 240, 16, 10) -- bg rectb(-1, self.y - 1, 242, 18, 8) -- border local txt, color, adjX = "Defeat!", 1, 0 if Winner then txt = "Victory" color = 5 adjX = 3 end plus.pShadow(txt, color + 1, self.x + adjX, self.y + 2, color, false, 2) end _G.Shine = {} function Shine:boot() self.angle = 0.0 -- to game over screen self.subAngle = math.rad(45) self.dist = 40.0 self.distAdd = 0.23 self.particles = {} self.cooldown = 0 end function Shine:update() -- rays self.angle = self.angle + math.rad(0.35) self.subAngle = self.subAngle - math.rad(0.35) self.dist = self.dist + self.distAdd if self.dist <= 40.0 or self.dist >= 60.0 then self.distAdd = -self.distAdd end -- new particles if self.cooldown > 0 then self.cooldown = self.cooldown - 1 else self.cooldown = math.random(60, 150) local id = math.random(2) local pos = { {99, 141, 35, 80}, {85, 155, 39, 73,}, {116, 124, 80, 106,}, } table.insert(self.particles, { x = math.random(pos[id][1], pos[id][2]), y = math.random(pos[id][3], pos[id][4]), flip = math.random(0, 1), radius = 0, add = 0.1, id = 0, }) end -- update particles for i, p in ipairs(self.particles) do p.id = p.id + p.add if p.id >= 5.9 then p.id = 2.9 p.add = -p.add end if p.id <= 0.0 then table.remove(self.particles, i) end end end function Shine:rays() -- for draw shine rays local tx2, ty2, tx3, ty3 = 0, 0, 0, 0 -- distance, top distance, color local dst, tdst, col = 0, 0 -- adjustment to angle, adjustment to top angle local adj, tadj = math.rad(36), math.rad(18) local angle = 0 for i = 1, 0, -1 do dst = Shine.dist - 5 * i tdst = 5 - i col = 10 + i angle = (i == 0) and Shine.angle or Shine.subAngle for j = 0, 3 do angle = angle + math.rad(90 * j) tx2 = 120 + dst * math.cos(angle) ty2 = 068 + dst * math.sin(angle) tx3 = 120 + dst * math.cos(angle + adj) ty3 = 068 + dst * math.sin(angle + adj) tri(120, 68, tx2, ty2, tx3, ty3, col) tri( 120 + (dst + tdst) * math.cos(angle + tadj), 068 + (dst + tdst) * math.sin(angle + tadj), tx2, ty2, tx3, ty3, col ) end end end function Shine:points() for i, p in ipairs(self.particles) do if math.floor(p.id) > 0 then spr(144 + math.floor(p.id), p.x - 3, p.y - 3, 11, 1, p.flip) end end end _G.Stars = {} function Stars:boot() local signal = 0 for i = 1, 35 do signal = ((math.random(2) == 2) and 1 or -1) Stars[i] = { x = 0, y = 0, choosed = false, upID = (math.random(10) / 100) * signal, colorID = math.random(7, 11) + math.random(9) / 10, update = function(self) self.colorID = self.colorID + self.upID if self.colorID <= 7.0 or self.colorID >= 11.9 then self.upID = -self.upID self.colorID = (self.colorID <= 7.0) and 7.0 or 11.9 end if not self.choosed then return end local adj = (BigBtnMode) and 2 or 0 local x1, y1, x2, y2 = self.x - adj, self.y - adj, self.x + adj, self.y + adj if Cursor.clicking and coli.touch(x1, y1, x2, y2) and #Panel.online == 0 then Achiev:progress(3) self.choosed = false end end, draw = function(self) local color = (self.choosed and math.floor(self.colorID) == 10) and 2 or math.floor(self.colorID) pix(self.x, self.y, color) end } end if lbit.getBool(3, "achiev1") then return end Stars[math.random(35)].choosed = true end function Stars:coli(a, b) for _, s in ipairs(Stars) do local newStar = {x = a, y = b, radius = 5} local oldStar = {x = s.x, y = s.y, radius = 5} if coli.circle(newStar, oldStar) then return false end end return true -- it is not colliding end function Stars:newPos() local x, y = 0, 0 local title, start = nil, nil for i = 1, 35 do repeat x, y = math.random(7, 232), math.random(3, 133) title = not coli.rectangle({x, y}, {61, 24, 118, 38}) start = not coli.rectangle({x, y}, {97, 88, 46, 24}) until Stars:coli(x, y) and title and start Stars[i].x = x Stars[i].y = y end end function Stars:update() for i = 1, #self do self[i]:update() end end function Stars:draw() for i = 1, #self do self[i]:draw() end end _G.Timer = {} function Timer:boot() self.start = 240 self.tic = 3600 self.sec = 0 self.min = 3 self.on = false self.upMin = true end function Timer:update() if not self.on then return end if self.tic > 0 then self.tic = self.tic - 1 self.sec = math.ceil(self.tic / 60) if self.sec == 60 and not self.upMin then self.upMin = true elseif self.sec == 59 and self.upMin then self.upMin = false self.min = self.min - 1 end return end if self.min == 0 then Winner = true Trans:start(2) self.on = false return end self.tic = 3600 end function Timer:draw(Y) local x, y = 105, 0 + (Y or 0) for i = 0, 1 do for j = 0, 1 do spr(501, x + 14 * j, y + 3 * i, 15, 1, j + 2 * i, 0, 2, 1) end end local sec = self.sec if sec == 60 then sec = "00" elseif sec < 10 then sec = "0"..sec end print("0"..self.min..":"..sec, x + 2, y + 3, 7) end _G.Trans = {} -- boot in start function Trans:start(id) if id == nil then -- boot game self.timer = 30 self.on = true self.id = 0 return end -- boot it self.timer = 0 self.on = true self.id = id self.enter = true end function Trans:shadow(id) id = id or 1 if id == 0 then cls() return end for y = 0, 16 do for x = 0, 29 do spr(id, x * 8, y * 8, 12) end end end function Trans:update() if self.on then if self.enter and self.timer < 30 then self.timer = self.timer + 1 if self.timer == 30 then self.enter = false Menu = self.id if self.id == 1 then MatchStart() elseif self.id == 2 then MatchEnd() end end elseif self.timer > 0 then local toSash = (Menu == 2 and (not Sash.on and self.timer < 24.0) or self.timer >= 24.0) if (Menu ~= 2 or toSash) then self.timer = self.timer - 1 end if self.timer == 0 then self.on = false end end end end function Trans:draw() if self.on then self:shadow(3 - math.floor(self.timer) // 8) end end _G.Welcome = {} function Welcome:up(full) WelcomeID = WelcomeID + 1 if WelcomeID > 4 or full then FirstRun = false lbit.setNum(1, 2, "system") end end function Welcome:update(click) if not click then return end local x, y = 0, 0 -- language if WelcomeID == 1 then x, y = 86, 52 for i = 0, 1 do if coli.touch(x, y + 17 * i, x + 68, y + 14 + 17 * i) then Sound(1) lbit.setNum(2 - i, 8, "config1") Language:updateAll() Welcome:up() return end end end -- configuration packages if WelcomeID == 2 then x, y = 74, 43 for i = 0, 2 do if coli.touch(x + 3, y + 25 + 8 * i, x + 89, y + 31 + 8 * i) then Sound(1) if i == 0 then ConfigPacks() -- computer elseif i == 1 then ConfigPacks(true) -- phone end -- none Welcome:up() return end end end -- download recommendad if WelcomeID == 3 then x, y = 62, 46 for i = 0, 1 do if coli.touch(x + 3 + 57 * i, y + 31, x + 56 + 57 * i, y + 39) then Sound(1) if i == 1 then local lang = "" if lbit.getBool(8, "config1") then lang = "Copy this link and paste it on your browser:" else lang = "Copie esse link e cole ele no seu navegador:" end trace("\n\n\n\n"..lang.."\n\nhttps://github.com/duckafire/Small_Projects/blob/main/tutorials/how-to-download-tic80.md\n\n\n", 12) end Welcome:up(i == 0) -- close / link return end end end -- message about link if WelcomeID == 4 then x, y = 64, 50 for i = 0, 1 do if coli.touch(x + 3 + 55 * i, y + 24, x + 54 + 55 * i, y + 24 + 8) then Sound(1) Welcome:up() if i == 1 then exit() end -- exit return end end end end function Welcome:draw(click) local x, y = 0, 0 -- language if WelcomeID == 1 then x, y = 83, 49 rect( x, y, 75, 38, 11) rectb(x, y, 75, 38, 8) local txt = {"Selecione um idioma:", "Portugues", "Select a language:", "English"} for i = 1, 4 do if i % 2 ~= 0 then rect(x + 3, y + 3 + 8.5 * (i - 1), 69, 15, 10) end plus.pCenter(txt[i], x + 37, y + 6 + 7 * (i - 1) + ((i > 2) and 3 or 0), 7, false, 1, true) end return end -- configuration packages if WelcomeID == 2 then x, y = 74, 43 local lang, lang2 = nil, "" if lbit.getBool(8, "config1") then lang = {"Select a \"package\"", "of configurations", "recommended (optional):"} lang2 = "None (manually)" else lang = {"Escolha um \"pacote\"", "de configuracoes", "recomendadas (opcional):"} lang2 = "Nenhum (manual)" end rect( x, y, 93, 51, 11) rectb(x, y, 93, 51, 8) plus.pList(lang, x + 45, y + 10, 7, 7, false, 1, true, 0, true) local opt = {Language.options[20], Language.options[21], lang2} for i = 1, 3 do rect(x + 3, y + 25 + 8 * (i - 1), 87, 7, 10) plus.pCenter(opt[i], x + 45, y + 28 + 8 * (i - 1), 7, false, 1, true) end return end -- download recommendad if WelcomeID == 3 then x, y = 62, 46 local lang, lang2 = nil, nil if lbit.getBool(8, "config1") then lang = { "Caso you are using the browser", "version, it is recommended that", "you install the \"local\"", "version, for a best experience", } lang2 = {"Not install", "Install"} else lang = { "Caso esteja usando a versao de ", "navegador, recomenda-se que", "instale a verssao \"local\",", "para uma melhor experiencia.", } lang2 = {"Nao instalar", "Instalar"} end rect( x, y, 117, 45, 11) rectb(x, y, 117, 45, 8) plus.pList(lang, x + 58, y + 13, 7, 7, false, 1, true, 0, true) for i = 0, 1 do rect( x + 3 + 57 * i, y + 31, 54, 9, 2 + 4 * i) rectb(x + 3 + 57 * i, y + 31, 54, 9, 1 + 4 * i) plus.pCenter(lang2[i + 1], x + 29 + 57 * i, y + 35, 12, false, 1, true) end return end -- message about link if WelcomeID == 4 then x, y = 64, 50 local lang, lang2 = nil, nil if lbit.getBool(8, "config1") then lang = { "A link to tutorial about how", "to install the \"local\"", "version was pasted on console." } lang2 = {"Look after", "Look now"} else lang = { "Um link para o tutorial de", "como baixar a versão local", "foi colado no console.", } lang2 = {"Ver depois", "Ver agora"} end rect( x, y, 113, 36, 11) rectb(x, y, 113, 36, 8) plus.pList(lang, x + 55, y + 10, 7, 7, false, 1, true, 0, true) for i = 0, 1 do rect( x + 3 + 55 * i, y + 24, 52, 9, 2 + 4 * i) rectb(x + 3 + 55 * i, y + 24, 52, 9, 1 + 4 * i) plus.pCenter(lang2[i + 1], x + 29 + 55 * i, y + 28, 12, false, 1, true) end return end end --========================================================-- --===================== ENTITIES =========================-- --========================================================-- -- LIST (a-z): -- Consu -- Doors -- Enemies -- Traps _G.Consu = {cool = 0, entities = {}, filled = {}} function Consu:add(item) self.cool = math.random(math.random(300, 600), 1200) table.insert(self.entities, item) end function Consu:hitbox(i, item, v, c) if Player.hp == 0 then return end local body = coli.newBody("rect", item.x, item.y, 8, 8) if Player:hitbox(body, true) then Sound(6) self.num(item.x, item.y, v, c) self.filled[item.y // 8][item.x // 8] = false item:bonus() table.remove(self.entities, i) end end function Consu:spawn() if self.cool > 0 then self.cool = self.cool - 1 return end if #self.entities >= 10 or 1 ~= 1 then return end local chance = math.random(4) if chance <= 3 then self.coin() elseif chance <= 4 then self.charge() end local item = self.entities[#self.entities] repeat item.x = math.random(1, 28) item.y = math.random(1, 15) until (fget(mget(item.x + Map.x, item.y + Map.y), 0) and not self.filled[item.y][item.x] and coli.distance(Player, {item.x * 8, item.y * 8}) > 35) self.filled[item.y][item.x] = true item.x = item.x * 8 item.y = item.y * 8 end function Consu.partSpeed(func) local speeds = {-5, -4, 3, 3, 4, 5} return (speeds[math.random(#speeds)] / 10) * func(math.rad(math.random(15, 360))) end function Consu.particles(x, y, co) local it = nil local sx, sy = 0, 0 for i = 1, math.random(20, 30) do sx, sy = Consu.partSpeed(math.cos), Consu.partSpeed(math.cos) it = { x = x, y = y, sx = sx, sy = sy, maxSx = sx, maxSy = sy, cool = 60, color = co + ((math.random(3) == 1) and 1 or 0) } function it:update(i) self.cool = self.cool - 1 if self.cool % 10 == 0 then self.sx = self.sx - self.maxSx * 0.1 self.sy = self.sy - self.maxSy * 0.1 end if self.cool <= 0 then table.remove(Effects.back, i) end self.x = self.x + self.sx self.y = self.y + self.sy end function it:draw() if (self.cool % 3 == 0 and self.cool < 10) or (self.cool % 2 == 1 and self.cool > 20 and self.cool <= 30) or self.cool > 30 then pix(self.x, self.y, self.color) end end table.insert(Effects.back, it) end end function Consu.num(x, y, v, c) local it = { x = x, y = y, minY = y - 8, cool = 60, value = "+"..v, color = c, } function it:update(i) if self.y > self.minY then self.y = self.y - 0.75 else self.cool = self.cool - 1 if self.cool <= 0 then table.remove(Effects.front, i) end end end function it:draw() if self.cool < 4 or (self.cool >= 8 and self.cool < 12) or self.cool > 15 then plus.pBorder(self.value, self.x, self.y, self.color, 0) end end table.insert(Effects.front, it) Consu.particles(it.x + 3, it.y + 3, c) end function Consu.coin() local it = { x = 16, y = 16, id = 1.0, spt = {474, 475, 476, 475}, } function it:bonus() Coins.inMatch = Coins.inMatch + 1 end function it:update(i) self.id = self.id + 0.075 if self.id >= 4.9 then self.id = 1.0 end Consu:hitbox(i, self, "1", 5) end function it:draw() local id = math.floor(self.id) spr(self.spt[id], self.x, self.y, 0, 1, ((id == 4) and 1 or 0)) end Consu:add(it) end function Consu.charge() local it = { x = 0, y = 0, id = 0.0, spt = 472, } function it:bonus() Achiev:progress(1) if Player.hp == 3 then Coins.inMatch = Coins.inMatch + 2 return end Player.hp = Player.hp + 1 end function it:update(i) self.id = self.id + 0.05 if self.id >= 1.9 then self.id = 0.0 end local v, c = "1", 12 if Player.hp == 3 then v, c = "2", 5 end Consu:hitbox(i, self, v, c) end function it:draw() spr(self.spt + math.floor(self.id), self.x, self.y, 0) end Consu:add(it) end _G.Doors = { back = {}, front = {}, tags = { { "gate", "trapdoor" }, {}, {}, {}, } } function Doors.gate(x, y) local it = { x = x * 8, y = y * 8, id = 0.0, spt = 498, back = false, rot = (mget(Map.x + x, Map.y + y)) - 80, timer = 30, newPart = 5, speedX = 1, -- position or negative speedY = 1, state = 1, -- 1: opening; 0: stopped; -1: closing online = false, group = "", smokeX = 0.0, smokeY = 0.0, minSmokeX = 0, minSmokeY = 0, maxSmokeX = 0, maxSmokeY = 0, entity = nil, } Map:swapTile(x, y, mget(Map.x + x, Map.y + y), true) function it:boot(enemy) self.online = true self.state = 1 self.group = "" self.timer = 30 self.newPart = 5 self.smokeX = self.x self.smokeY = self.y self.minSmokeX = self.x self.minSmokeY = self.y self.maxSmokeX = self.x self.maxSmokeY = self.y self.entity = enemy -- smoke position (trap position, max, min) local tox = {{0, -8, 16, 1}, {232, 240, 216, -1}} local toy = {{0, -8, 16, 1}, {128, 136, 112, -1}} for i = 1, 2 do if self.x == tox[i][1] then self.smokeX, self.minSmokeX, self.maxSmokeX, self.speedX = tox[i][2], tox[i][2], tox[i][3], tox[i][4] break end if self.y == toy[i][1] then self.smokeY, self.minSmokeY, self.maxSmokeY, self.speedY = toy[i][2], toy[i][2], toy[i][3], toy[i][4] break end end end function it:update(i) if not self.online then return end -- moving entity if self.timer == 0 or self.state == -1 then self.smokeX = (self.maxSmokeX - math.floor(self.smokeX) ~= 0.0) and self.smokeX + 0.25 * self.speedX or self.maxSmokeX self.smokeY = (self.maxSmokeY - math.floor(self.smokeY) ~= 0.0) and self.smokeY + 0.25 * self.speedY or self.maxSmokeY end -- smoke particles self.newPart = self.newPart - 1 if self.newPart == 0 then self.newPart = 5 NewSmokeParticle(math.floor(self.smokeX), math.floor(self.smokeY)) end -- opening/closing if self.timer > 0 then self.timer = self.timer - 1 -- sprite index if self.timer > 0 and self.timer % 10 == 0 and self.state ~= 0 then self.id = self.id + self.state end if self.timer == 0 then -- stop entity if self.state == -1 then self.state = 0 self.timer = 120 -- create entity elseif self.state == 0 then self.smokeX, self.smokeY = coli.mapAlign({self.smokeX, self.smokeY}) self.entity(self.smokeX, self.smokeY) self.online = false return end end if self.state ~= 0 then return end end -- start close animation if math.floor(self.smokeX) == self.minSmokeX + ((self.minSmokeX < 0) and 16 or -16) or math.floor(self.smokeY) == self.minSmokeY + ((self.minSmokeY < 0) and 16 or -16) then self.state = -1 self.timer = 30 end end function it:draw() Enemies.sprite(self) end return it end function Doors.trapdoor(x, y) local it = { x = x * 8, y = y * 8, id = 0.0, spt = 482, back = true, newPart = 5, online = false, timer = 300, entity = nil, } Map:swapTile(x, y, mget(Map.x + x, Map.y + y), false, 0) function it:boot(enemy) self.online = true self.timer = 300 self.newPart = 5 self.entity = enemy end function it:update(i) if not self.online then return end -- smoke particles self.newPart = self.newPart - 1 if self.newPart == 0 then self.newPart = 5 NewSmokeParticle(self.x, self.y) end self.timer = self.timer - 1 if self.timer < 0 then self.online = false self.entity(self.x, self.y) end end function it:draw() Enemies.sprite(self) end return it end _G.Enemies = { cool = 0, default = {}, kamikaze = {}, unities = {}, tags = { { -- theme 1 default = {"auxUnity", "newUnity", "observer"}, kamikaze = {"oldUnity"}, }, {}, {}, {}, }, quantity = { auxUnity = 0, newUnity = 0, observer = 0, oldUnity = 0, } } function Enemies.sprite(obj) spr(obj.spt + math.floor(obj.id), obj.x, obj.y, obj.chroma or 15, 1, obj.flip, obj.rot) end function Enemies.unities:cpu(m, M) -- tile collision self.side = coli.tileCross(self, false, 1, Map.x, Map.y) self.alignX, self.alignY = coli.mapAlign(self) if self.side[self.direction] then self.timerDir = 0 end -- timer to swap movement direction if not (self.timerDir <= 0 and self.x % 8 == 0 and self.y % 8 == 0) then self.timerDir = self.timerDir - 1 return end self.timerDir = math.random(m, M) local id = self.direction + 1 -- left/right index local left = {2, 3, 0, 1} local right = {3, 2, 1, 0} local back = {1, 0, 3, 2} -- current currections local cleft, cright = left[id], right[id] -- closed movement if self.side[cleft] and self.side[cright] then if self.side[id - 1] then self.direction = back[id] end return end -- specific for "auxUnity" if self.auxcpu ~= nil then if math.random(3) == 3 then if self:auxcpu() then self.max = math.random(075, 100) self.MAX = math.random(110, 135) return end end self.max = math.random(20, 40) self.MAX = math.random(50, 70) end -- free movement (for both) if self.side[cleft] or self.side[cright] then self.direction = (self.side[cleft]) and cright or cleft return end local rar = math.random(((self.side[id - 1]) and 2 or 4)) self.direction = (rar == 1) and cleft or (rar == 2) and cright or self.direction end function Enemies.unities:auxcpu(id, cond) if id == nil then local origin = { self.y > Player.y, self.y < Player.y, self.x > Player.x, self.x < Player.x, } -- VERY random!! local conds, rar = {}, 0 for i = 1, 4 do rar = math.random(#origin) table.insert(conds, origin[rar]) table.remove(origin, rar) end for i = 1, 4 do if self:auxcpu(i - 1, conds[i]) then return true end end return false end if not self.side[id] and cond then self.direction = id return true end return false end function Enemies.unities:sensors(i) local addX = (Player.alignX < self.alignX) and -8 or 8 local addY = (Player.alignY < self.alignY) and -8 or 8 local x, y = 0, 0 for path = 0, 1 do x, y = self.x, self.y while true do -- update position x = (path == 0) and x + addX or x y = (path == 1) and y + addY or y -- the tile is a wall if not fget(mget(x // 8 + Map.x, y // 8 + Map.y), 0) then break end -- seeing the player if Player.alignX == x and Player.alignY == y then -- targets if self.targets == nil then -- remove all targets after it (targets) if i <= #self.mom.targets then ClearTable(self.mom.targets, i + 1) end -- newUnity else self.targets = {} -- clear all end -- position; align; behavior table.insert(self.targets or self.mom.targets, { x = x, y = y, -- screen positions alignX = x, alignY = y, -- aligned positions sensors = self.sensors, -- behaviror mom = (not self.targets) and self.mom or self -- origin }) break end end end end function Enemies.unities:tyres(c1, c2, c3, c4) if c1 then self.y = self.y - self.speed elseif c2 then self.y = self.y + self.speed elseif c3 then self.x = self.x - self.speed elseif c4 then self.x = self.x + self.speed else return true -- above the target end return false end function Enemies.auxUnity(x, y) local it = { x = x, y = y, width = 8, height = 8, speed = 0.25, spt = 322, id = 0, name = "auxUnity", removing = 1, alignX = 0, alignY = 0, targets = {}, direction = math.random(0, 3), timerDir = 0, -- for swap it moviment direction side = nil, cpu = Enemies.unities.cpu, auxcpu = Enemies.unities.auxcpu, tyres = Enemies.unities.tyres, sensors = Enemies.unities.sensors, radius = 0, slowCool = 240, maxSlowCoo = 240, max = math.random(075, 100), MAX = math.random(110, 135), } function it:particles(qtt, QTT) local angle, color, it = 0.0, 0, nil local shades = {5, 12, 1} for i = 1, math.random(qtt, QTT) do angle = math.rad(math.random(360)) local it = { x = self.x + 3 + math.random(0, 1), y = self.y + 3 + math.random(0, 1), sx = (math.random(5, 10) / 10) * math.cos(angle), sy = (math.random(5, 10) / 10) * math.sin(angle), timer = math.random(15, 35), color = shades[self.id + 1] + ((math.random(3) == 3) and 1 or 0), } function it:update(i) self.x = self.x + self.sx self.y = self.y + self.sy self.timer = self.timer - 1 if self.timer == 0 or fget(mget(self.x // 8 + Map.x, self.y // 8 + Map.y), 1) then table.remove(Effects.back, i) end end function it:draw() pix(self.x, self.y, self.color) end table.insert(Effects.back, it) end end function it:update(i) -- 0 >: warnings | 0 <=: action self.maxSlowCoo = (self.slowCool == -50) and math.random(240, 420) or self.maxSlowCoo self.slowCool = (self.slowCool > -50) and self.slowCool - 1 or self.maxSlowCoo self.radius = (self.slowCool <= 00) and self.radius + 0.4 or 0 -- warning effects local max = self.maxSlowCoo - 10 if self.slowCool == 040 then self.id = 2 self:particles(20, 25) elseif self.slowCool == 150 then self.id = 1 self:particles(15, 18) elseif self.slowCool == max then self.id = 0 self:particles(08, 12) end -- move it self:cpu(self.max, self.MAX) local d = self.direction self:tyres(d == 0, d == 1, d == 2, d == 3) -- attack if math.floor(self.radius) == 0 then return end for i = 0, 1 do if Player:startDebuff(coli.newBody("circ", self.x + 3 + i, self.y + 3, math.floor(self.radius)), true) then break end end end function it:draw() Enemies.sprite(self) if self.radius > 0 then for i = 0, 1 do circb(self.x + 3 + i, self.y + 3, self.radius, 3) end end end table.insert(Enemies.default, it) end function Enemies.newUnity(x, y) local it = { x = x, y = y, speed = 0.5, spt = 354, id = 0, chroma = 2, name = "newUnity", removing = 1, alignX = 0, alignY = 0, targets = {}, direction = math.random(0, 3), timerDir = 0, -- for swap it moviment direction side = nil, cpu = Enemies.unities.cpu, tyres = Enemies.unities.tyres, sensors = Enemies.unities.sensors, } function it:update(i) if #self.targets == 0 then self:cpu(45, 90) self:sensors() local d = self.direction self:tyres(d == 0, d == 1, d == 2, d == 3) else local t = self.targets[1] if self:tyres(t.y < self.y, t.y > self.y, t.x < self.x, t.x > self.x) then table.remove(self.targets, 1) end end Player:hitbox(coli.newBody("rect", self.x + 1, self.y, 6, 8)) for i, v in ipairs(self.targets) do v:sensors(i) end self.id = (#self.targets > 0) and 1 or 0 end function it:draw() Enemies.sprite(self) end table.insert(Enemies.default, it) end function Enemies.oldUnity(x, y) local it = { x = x, y = y, speed = 0.25, spt = 306, id = 0, name = "oldUnity", removing = 1, lifeTimer = math.random(1800, 3600), rangeTimer = 90, -- (-) finalTimer = 0, -- (+) willExplode = false, } function it:update(i) -- sprite index self.id = (self.willExplode and Player.hp > 0) and self.finalTimer // 45 + 1 or 0 -- speed buff if self.finalTimer == 30 or self.finalTimer == 60 then self.sped = self.speed + (self.id / 20) * 0.7 -- +0.035; +0.07 end -- move it if Player.hp > 0 then local angle = math.atan2((Player.y + 3) - (self.y + 3), (Player.x + 3) - (self.x + 3)) local sx, sy = self.speed * math.cos(angle), self.speed * math.sin(angle) if not coli.circle({self.x + 3, self.y + 3}, {Player.x + 3, Player.y + 3, 3}) then local body = {self.x + 1, self.y, 6, 8} if coli.box360(sx, nil, body, 0, Map.x, Map.y) then self.x = self.x + sx end if coli.box360(nil, sy, body, 0, Map.x, Map.y) then self.y = self.y + sy end end end Traps.effects.conveyorBelt(self) -- explosion (it cannot be broken after that start) if self.willExplode then self.finalTimer = self.finalTimer + 1 if self.finalTimer == 90 then NewExplosion(self.x + 3, self.y + 3, ((self.lifeTimer == 0) and 45 or 30)) Enemies.quantity.oldUnity = Enemies.quantity.oldUnity - 1 table.remove(Enemies.kamikaze, i) end return end -- countdown to auto destroy self.lifeTimer = self.lifeTimer - 1 if self.lifeTimer == 0 then self.willExplode = true return end -- player is very closeness if Player:hitbox(coli.newBody("circ", self.x + 3, self.y + 3, 25), true) then if self.rangeTimer > 0 then self.rangeTimer = self.rangeTimer - 1 self.willExplode = (self.rangeTimer == 0) end return end self.rangeTimer = 90 end function it:draw() Enemies.sprite(self) end table.insert(Enemies.kamikaze, it) end function Enemies.observer(x, y) local it = { x = x, y = y, speed = 0.5, id = 0, spt = 338, name = "observer", removing = 1, tics = 60, radar = {}, radarID = nil, -- index of the particle that find the player targets = {}, curTileX = 0, -- to get it tile position curTileY = 0, lastTileX = -1, lastTileY = -1, pCurTileX = 0, -- to get the positions of the particle that find player pCurTileY = 0, pLastTileX = -1, pLastTileY = -1, } function it:cmpTilePos(objID) -- observer if objID == 0 then return (self.curTileX == self.lastTileX and self.curTileY == self.lastTileY) end -- player if objID == 1 then return (self.pCurTileX == self.pLastTileX and self.pCurTileY == self.pLastTileY) end -- last target if #self.targets == 0 then return false end local ltar = self.targets[#self.targets] local x, y = coli.mapAlign(coli.newBody("simp", ltar.x, ltar.y)) return (Player.alignX == x and Player.alignY == y) end function it:newWave() self.tics = (#self.targets > 0) and 30 or 60 local it = nil local angle = math.atan2((Player.y + 3) - (self.y + 3), (Player.x + 3) - (self.x + 3)) local angl2 = 0.0 for i = -3, 3 do it = { x = self.x + 3, y = self.y + 3, originX = self.x + 3, originY = self.y + 3, } angl2 = angle - math.rad(i * 2) it.sx = 2.5 * math.cos(angl2) it.sy = 2.5 * math.sin(angl2) function it:update(i) for i = 1, 6 do self.x = self.x + self.sx self.y = self.y + self.sy if fget(mget(self.x // 8 + Map.x, self.y // 8 + Map.y), 1) then return 1 end if coli.rectangle(self, {Player.x + 1, Player.y, 6, 8}) then return 2 end end return 0 end function it:draw() pix(self.x, self.y, 4) end table.insert(self.radar, it) end end function it:updateWave() local rtn = 0 for i, v in ipairs(self.radar) do rtn = v:update(i) if rtn == 1 then table.remove(self.radar, i) elseif rtn == 2 then self.radarID = i break end end end function it:getPath() -- clear current path local ltarx, ltary = nil, nil if #self.targets > 0 then ltarx, ltary = self.targets[1].x, self.targets[1].y ClearTable(self.targets) end self.x = math.floor(self.x) self.y = math.floor(self.y) -- set particle position local part = self.radar[self.radarID] part.x = (ltarx ~= nil) and part.originX or self.x + 3 part.y = (ltary ~= nil) and part.originY or self.y + 3 -- set path to target (player) while true do part.x = part.x + part.sx part.y = part.y + part.sy self.curTileX, self.curTileY = coli.mapAlign(part) -- set a new tile to path if self.curTileX ~= self.lastTileX or self.curTileY ~= self.lastTileY then self.lastTileX, self.lastTileY = coli.mapAlign(coli.newBody("simp", self.curTileX, self.curTileY)) table.insert(self.targets, {x = self.curTileX, y = self.curTileY}) end if Player:hitbox({part.x, part.y, 1, 1}, true) or not coli.rectangle(part, {0, 0, 240, 136}) then break end end -- same position of the enemy (origin) table.remove(self.targets, 1) ClearTable(self.radar) return end function it:followPath() local tar = self.targets[1] local sx = (tar.x < self.x) and -self.speed or self.speed local sy = (tar.y < self.y) and -self.speed or self.speed -- move it if self.x ~= tar.x and coli.box360(sx, nil, self, 0, Map.x, Map.y) then self.x = self.x + sx return end if self.y ~= tar.y and coli.box360(nil, sy, self, 0, Map.x, Map.y) then self.y = self.y + sy return end table.remove(self.targets, 1) if not self:cmpTilePos(2) then self:newWave() end end function it:update(i) self.id = (#self.targets == 0) and 0 or 1 if Player.hp == 0 then return end self.curTileX, self.curTileY = coli.mapAlign(self) self.pCurTileX, self.pCurTileY = coli.mapAlign(Player, true) if #self.targets > 0 then self:followPath() else Traps.effects.conveyorBelt(self) end Player:hitbox({self.x, self.y, 8, 8}) if #self.radar > 0 or not self:cmpTilePos(0) or (self.targets[1] == nil and (not self:cmpTilePos(1) or not self:cmpTilePos(2))) then self:updateWave() end if self.radarID ~= nil then self:getPath() self.radarID = nil self.tics = 30 end self.tics = (self.tics > 0) and self.tics - 1 or 0 local allCmpTilePos = self:cmpTilePos(0) and self:cmpTilePos(1) and self:cmpTilePos(2) if self.tics == 0 and #self.targets == 0 and not allCmpTilePos then self:newWave() end if not self:cmpTilePos(0) then self.lastTileX = self.curTileX self.lastTileY = self.curTileY end if not self:cmpTilePos(1) then self.pLastTileX = self.pCurTileX self.pLastTileY = self.pCurTileY end end function it:draw() Enemies.sprite(self) end table.insert(Enemies.default, it) end function Enemies:check(chance, down, name, isFirst) if isFirst or math.random(100) <= chance - math.floor(chance * (down * Enemies.quantity[name])) then -- groups local g1, g2 = nil, nil local door, group, qtt = nil, nil, 0 -- randomize the type of the doors if math.random(2) == 2 then g1 = Doors.back g2 = Doors.front else g1 = Doors.front g2 = Doors.back end group = g1 for i = 0, 1 do if #group > 0 then -- random door repeat door = group[math.random(#group)] qtt = qtt + 1 if qtt == 10 then break end until (not door.online) -- a door was finded if door ~= nil and qtt < 10 then break end -- none valid door was finded elseif i == 1 then return false end group = g2 end -- set enemy to door Enemies.quantity[name] = Enemies.quantity[name] + 1 door:boot(Enemies[name]) return true end return false end function Enemies:spawn() if self.cool > 0 then self.cool = self.cool - 1 return end -- maximum of the enemies local qtt = #self.default + #self.kamikaze if qtt > ((Map.curMap == 3) and 5 or 10) then return end self.cool = math.random(math.random(600, 800), 900 + math.random(60, 180) * qtt) -- first enemy if qtt == 0 and Map.curMap ~= 3 then if Map.curTheme then self:check(1, 0.0, "newUnity", true) end return end local origin = { {20, 0.15, "auxUnity"}, {35, 0.60, "newUnity"}, {15, 0.50, "observer"}, {30, 0.05, "oldUnity"}, } local ran, id = {}, 0 for i = 1, #origin do id = (#origin > 1) and math.random(#origin) or 1 table.insert(ran, origin[id]) table.remove(origin, id) end -- random enemy for i = 1, 10 do for j = 1, #ran do if self:check(table.unpack(ran[j])) then return end end end -- none enemy was spawned self.cool = math.floor(self.cool * 0.25) end _G.Traps = { back = {}, front = {}, effects = {}, tags = { -- front, front, back {"crusher", "incinerator", "conveyorBelt"}, -- theme 1 {}, {}, {}, } } function Traps.effects.conveyorBelt(obj) local points = { {obj.x + 2, obj.y + 2}, -- top-left {obj.x + 5, obj.y + 2}, -- top-right {obj.x + 2, obj.y + 5}, -- bottom-left {obj.x + 5, obj.y + 5}, -- bottom-right } local tile, sides = 0, coli.tileCross(obj, false, 0, Map.x, Map.y) for i = 1, 4 do tile = mget(points[i][1] // 8 + Map.x, points[i][2] // 8 + Map.y) if fget(tile, 2) then if tile == 26 and sides[0] then obj.y = obj.y - 0.25 -- top elseif tile == 27 and sides[3] then obj.x = obj.x + 0.25 -- right elseif tile == 28 and sides[1] then obj.y = obj.y + 0.25 -- bottom elseif tile == 29 and sides[2] then obj.x = obj.x - 0.25 -- left end return end end end function Traps.vision(isX, signal, obj, adj) local x, y = obj.x, obj.y local spdX = (( isX) and 8 or 0) * signal local spdY = ((not isX) and 8 or 0) * signal local adjX = (not isX) and adj or 0 local adjY = ( isX) and adj or 0 local it = nil adjX = adjX or 0 -- reduce/increase the dimensions adjY = adjY or 0 if isX then -- horizontal it = coli.newBody("rect", x + spdX, y, 0, 8) else -- vertical it = coli.newBody("rect", x, y + spdY, 8, 0) end local steps = 0 while (fget(mget((x + spdX) // 8 + Map.x, (y + spdY) // 8 + Map.y), 0)) do steps = steps + 1 if isX then x = x + spdX it.width = it.width + 8 else y = y + spdY it.height = it.height + 8 end end if spdX < 0 or spdY < 0 then it.x = x it.y = y end it.x = it.x + adjX it.y = it.y + adjY it.width = it.width - adjX * 2 it.height = it.height - adjY * 2 it.steps = steps return it end function Traps.crusher(x, y) local it = { x = x * 8, y = y * 8, id = 0.0, spt = 258, back = false, chroma = 15, vision = {}, steps = 0, direction = 0, } Map:swapTile(x, y, mget(Map.x + x, Map.y + y)) function it:attack() Player:hitbox(coli.newBody("rect", self.x, self.y, 8, 8)) end function it:setVision() self.vision[0] = Traps.vision(false, -1, it, 1) -- top self.vision[1] = Traps.vision(false, 1, it, 1) -- bottom self.vision[2] = Traps.vision(true, -1, it, 1) -- left self.vision[3] = Traps.vision(true, 1, it, 1) -- right end function it:ckVision(cond, id) if not cond then return end local v = self.vision[id] if v.width ~= 0 and v.height ~= 0 and Player:hitbox(v, true) then Sound(10) self.id = 1.0 self.steps = self.vision[id].steps self.direction = id end end function it:update(i) if self.id < 2.0 then self:attack() end if self.id == 0.0 then self:ckVision(Player.y < self.y, 0) self:ckVision(Player.y > self.y, 1) self:ckVision(Player.x < self.x, 2) self:ckVision(Player.x > self.x, 3) if self.id ~= 1.0 then return end end -- sprite animation if math.floor(self.id) ~= 2 then self.id = self.id + 0.029 if self.id >= 4.0 then self.id = 0.0 self:setVision() end return -- move it else if self.steps > 0 then self.steps = self.steps - 1 local speed = (self.direction % 2 == 0) and -8 or 8 if self.direction <= 1 then self.y = self.y + speed else self.x = self.x + speed end end if self.steps == 0 then self.id = 3.0 end end self:attack() end function it:draw() Enemies.sprite(self) end it:setVision() return it end function Traps.incinerator(x, y) local it = { x = x * 8, y = y * 8, id = 0.0, spt = 274, back = false, timer = 0, chroma = 15, atkID = 0, attacking = false, rot = mget(Map.x + x, Map.y + y) - 23 } local horizontal = (it.rot == 0 and fget(mget(Map.x + x - 1, Map.y + y), 0)) local vertical = (it.rot == 1 and fget(mget(Map.x + x, Map.y + y - 1), 0)) it.flip = (horizontal or vertical) and 1 + it.rot or 0 local tags = {"attack", "vision"} for i = 1, 2 do if it.flip == 2 and it.rot == 1 then it[tags[i]] = Traps.vision(false, -1, it, i) -- top elseif it.flip == 0 and it.rot == 1 then it[tags[i]] = Traps.vision(false, 1, it, i) -- bottom elseif it.flip == 1 and it.rot == 0 then it[tags[i]] = Traps.vision(true, -1, it, i) -- left elseif it.flip == 0 and it.rot == 0 then it[tags[i]] = Traps.vision(true, 1, it, i) -- rigth end end Map:swapTile(x, y, mget(Map.x + x, Map.y + y), true) function it:coup(i) local atk = coli.newBody("rect", self.attack.x, self.attack.y, self.attack.width, self.attack.height) atk.timer = 90 atk.momID = i function atk:update(i) Player:hitbox(atk) self.timer = self.timer - 1 if self.timer <= 0 then Traps.front[self.momID].id = 3.0 Traps.front[self.momID].attacking = false table.remove(Attacks.front, i) end end function atk:draw() rect(self.x, self.y, self.width, self.height, 12) end table.insert(Attacks.front, atk) end function it:update(i) if Player:hitbox(self.vision, true) and self.id == 0.0 then Sound(10) self.id = 1.0 end if self.id > 0.0 and math.floor(self.id) ~= 2 then self.id = self.id + 0.029 if self.id >= 4.0 then self.id = 0.0 return end end if self.id >= 2.0 and self.id < 3.0 and not self.attacking then self.attacking = true self:coup(i) end end function it:draw() Enemies.sprite(self, self.flip, self.rot) end return it end function Traps.conveyorBelt(x, y) local it = { x = x * 8, y = y * 8, id = 0.0, spt = 290, back = true, chroma = -1, rot = mget(Map.x + x, Map.y + y) - 26, } function it:update(i) self.id = self.id + 0.15 if self.id >= 2.9 then self.id = 0.0 end end function it:draw() Enemies.sprite(self, nil, self.rot) end return it end --========================================================-- --======================= BOSSES =========================-- --========================================================-- _G.Boss = {} function Boss:set() if Map.curMap ~= 3 then return end Boss:boot() if Map.curTheme == 1 then Boss:supervisor() return end end function Boss:boot() self.x = 0 self.y = 0 self.spt = 0 self.id = 0 self.tags = {} self.openCool = math.random(540, 660) self.closeCool = 0 self.atkTimer = 0 self.atkID = 0 self.lastAtk = 0 self.attacking = false self.attacks = {back = {}, front = {}} -- supervisor self.coreX = nil self.coreY = nil self.spt2 = nil self.cannonade = nil self.lazerwall = nil self.overflows = nil end function Boss:setMethods() function self:reload(id) local atk = self[self.tags[self.atkID]] if id == 1 then atk.start = 30 atk.color = 12.0 atk.radius = 0.0 atk.cool = 0 return end if id == 2 then atk.distance = 16 atk.quantity = 0.0 atk.angle = math.rad(45) atk.moveAngle = 0.0 atk.speed = math.rad(1) return end atk.spawn = true end function self:getGroup(y) return self.attacks[ ((y <= self.y) and "back" or "front") ] end function self:timer(tag, extra) if self[tag] == 0 then return false end self[tag] = self[tag] - 1 return self[tag] == 0 or extra end end function Boss:supervisor() self.x = 112 self.y = 65 self.spt = 370 self.tags = {"cannonade", "lazerwall", "overflows"} self.coreX = 120 self.coreY = 35 self.spt2 = 340 self.cannonade = {cool = 0} self.lazerwall = {cool = 0} self.overflows = {cool = 0} self.cannonade.start = 30 self.cannonade.color = 12.0 self.cannonade.radius = 0.0 self.lazerwall.speed = math.rad(1) self.lazerwall.angle = math.rad(45) self.lazerwall.distance = 16 self.lazerwall.quantity = 0.0 self.lazerwall.moveAngle = 0.0 self.overflows.spawn = true function Boss:update() -- pre-attack if self:timer("openCool", self.openCool == 20) then if self.id == 0 then Sound(11) end self.id = self.id + 1 if self.openCool == 0 then self.atkTimer = math.random(600, 720) -- 10-12s repeat self.atkID = math.random(3) until (self.atkID ~= self.lastAtk) self.lastAtk = self.atkID self:reload(self.atkID) end -- attacking elseif self:timer("atkTimer") then self.id = 1 self.closeCool = 20 -- pos-attack elseif self:timer("closeCool") then self.id = 0 self.openCool = math.random(480, 720) -- 8-12s end if self.atkTimer > 0 then self[self.tags[self.atkID]]:update() end for i, v in ipairs(self.attacks.back) do v:update(i) end for i, v in ipairs(self.attacks.front) do v:update(i) end end function Boss:draw() for i, v in ipairs(self.attacks.back) do v:draw() end -- Supervisor if self.spt2 ~= nil then spr(self.spt2, self.x, self.y - 23, 15, 2, 0, 0, 1, 2) -- body spr(self.spt + self.id, self.x, self.y - 38, 15, 2) -- core -- other bosses else end for i, v in ipairs(self.attacks.front) do v:draw() end end function Boss.cannonade:update() if self.start > 0 then self.start = self.start - 1 -- eletric impulse if self.start == 29 then local it = { x = Boss.coreX, y = Boss.coreY, color = 0.0, radius = 0, } function it:update(i) if self.radius > 150 then table.remove(Boss.attacks.front, i) end self.radius = self.radius + 2 if self.radius > 10 then self.color = self.color + 0.5 end end function it:draw() circb(self.x, self.y, self.radius, math.floor(self.color)) end table.insert(Boss.attacks.front, it) end return end Boss.cannonade.cool = Boss.cannonade.cool - 1 if Boss.cannonade.cool > 0 then return end Sound(12) Boss.cannonade.cool = 15 -- spawn projectile local it = { x = Boss.coreX, y = Boss.coreY, tarX = Player.x + 3, tarY = Player.y + 3, speed = -5, minHeight = (Player.y + 3) - 130, group = nil, } function it:update(i) if (self.y > self.minHeight and self.speed < 0) or self.speed > 0 then self.y = self.y + self.speed if self.speed > 0 and self.y > self.tarY then NewExplosion(self.x, self.y, 10) table.remove(self.group, i) end return end self.x = self.tarX self.y = self.minHeight self.speed = -self.speed self.group = Boss:getGroup(it.tarY) table.insert(self.group, self) table.remove(Boss.attacks.front, i) end function it:draw() circ(self.x, self.y, 2, 7) end it.group = nil table.insert(Boss.attacks.front, it) end function Boss.lazerwall:update() if self.quantity >= 90.0 then self.angle = self.angle + self.speed end if self.quantity < 90.0 then self.quantity = self.quantity + 0.5 end self.speed = self.speed + math.rad(0.00001) if math.floor(self.quantity * 10) % 100 == 0 and self.quantity < 90.0 then local it, adj = nil, 0.0 local dist = self.distance self.distance = self.distance + 16 self.moveAngle = (self.moveAngle == 0.0) and math.pi / 2 or 0.0 for i = 1, 2 do it = { x = 120, y = 68, centerX = Boss.x + 8, centerY = Boss.y + 2, radius = 0, bradius = 0, angleID = math.pi * (i - 1), moveAngle = self.moveAngle, distance = dist, gather = false, } function it:update(i) self.x = self.centerX + self.distance * math.cos(Boss.lazerwall.angle + self.angleID - self.moveAngle) self.y = self.centerY + self.distance * math.sin(Boss.lazerwall.angle + self.angleID - self.moveAngle) if Boss.atkTimer == 0 then self.gather = true end if math.floor(self.bradius) < 5 then self.bradius = self.bradius + 0.1 end if (math.floor(self.bradius) >= 5 and math.floor(self.radius) < 5) or self.gather then self.radius = self.radius + ((self.gather) and -0.1 or 0.1) end if self.radius > 1.0 then Player:hitbox(self) end if self.radius <= 0.0 and self.gather then table.remove(Boss.attacks.back, i) end end function it:draw() if self.radius > 0.0 then circ(self.x, self.y, self.radius, 1) end if math.floor(self.radius) < 5 and not self.gather then circb(self.x, self.y, self.bradius, 1) end end table.insert(Boss.attacks.back, it) end end end function Boss.overflows:update() if not self.spawn then return end self.spawn = false local it, angle, adjTar = nil, 0, 0 for i = 1, 4 do adjTar = 120 * (i - ((i < 3) and 1 or 3)) it = { x = Boss.coreX, y = Boss.coreY, tarX = math.random(16 + adjTar, 104 + adjTar), tarY = math.random(16, 48) + ((i <= 2) and 0 or 64), speed = 3, showLine = math.random(15, 25), hideLine = math.random(15, 25), } angle = math.atan2(it.tarY - it.y, it.tarX - it.x) it.sx = it.speed * math.cos(angle) it.sy = it.speed * math.sin(angle) function it:energy() local it = { x = self.x, y = self.y, radius = 0, fradius = 0, addRad = 0.25, maxRad = math.random(15, 25), aniRad = {0, -10}, } function it:update(i) for i = 1, 2 do self.aniRad[i] = self.aniRad[i] + 0.1 if self.aniRad[i] > self.radius then self.aniRad[i] = 0 end end if Boss.atkTimer <= 0 and self.addRad > 0 then self.addRad = -self.addRad end if self.radius > 1.0 then Player:startDebuff(self, true) end -- expand/gather radius if self.fradius < self.maxRad or (self.fradius > 0 and self.addRad < 0) then self.radius = self.radius + self.addRad self.fradius = math.floor(self.radius) end if self.addRad < 0 and self.fradius <= 0 then table.remove(Boss.attacks.back, i) end end function it:draw() for i = 1, 2 do if self.aniRad[i] > 0 then circb(self.x, self.y, math.floor(self.aniRad[i]), 3) end end circb(self.x, self.y, self.fradius, 3) end table.insert(Boss.attacks.back, it) end function it:update(i) if self.hideLine > 0 then self.showLine = (self.showLine > 0) and self.showLine - 1 or 0 if self.showLine == 0 then self.hideLine = (self.hideLine > 0) and self.hideLine - 1 or 0 end return end local moveX = ((self.sx < 0 and self.x > self.tarX) or (self.sx > 0 and self.x < self.tarX)) local moveY = ((self.sy < 0 and self.y > self.tarY) or (self.sy > 0 and self.y < self.tarY)) if moveX then self.x = self.x + self.sx end if moveY then self.y = self.y + self.sy end if not moveX and not moveY then self:energy() table.remove(self.group, i) end end function it:draw() if self.hideLine > 0 and self.showLine == 0 then line(self.x, self.y, self.tarX, self.tarY, 7) return end if self.hideLine == 0 then circ(self.x, self.y, 2, 7) end end it.group = Boss:getGroup(it.tarY) table.insert(it.group, it) end end end --========================================================-- --======================== RUN ===========================-- --========================================================-- local function UPDATE() BigBtnTim = (BigBtnTim > 0.0) and BigBtnTim - 0.01 or 0.0 local click = Cursor:update() -- @Ualways Achiev:check() -- @U0 if Menu % 2 == 0 then Stars:update() BigBtnMode = lbit.getBool(1, "config2", 2) TextAnimID = TextAnimID + 0.08 TextAnimCO = TextAnimCO + ((TextAnimUP) and -0.08 or 0.08) if TextAnimID >= 2.9 then TextAnimID = 1.0 end if TextAnimCO <= 8.0 or TextAnimCO >= 11.9 then TextAnimUP = not TextAnimUP end end -- @U1 if Menu == 0 then BtnTop = (BtnTop > 14.1) and BtnTop - 0.1 or 14.0 BtnBottom = (BtnBottom > 30.1) and BtnBottom - 0.1 or 30.0 BtnLeft = (BtnLeft > 46.1) and BtnLeft - 0.1 or 46.0 BtnRight = (BtnRight > 46.1) and BtnRight - 0.1 or 46.0 -- start local isBig = (BigBtnMode) and 10 or 0 if #Panel.online == 0 and click and not FirstRun then if coli.touch(98 - isBig, 97 - isBig, 143 + isBig, 104 + isBig) then Sound(2) Trans:start(1) end end if FirstRun then Welcome:update(click) return end if Joystick.config then if Joystick.confPk then Joystick:confPackUpdate(click) else Joystick:confMenuUpdate(click) end else Panel:update(click) end return end -- @U2 if Menu == 1 then Player:animSkin(true) poke(0x03ff8, ((coli.touch(0, 0, 239, 135)) and 0 or 2)) if MatchCountdown(true) then return end Timer:update() Enemies:spawn() Consu:spawn() Portal() ClearPortalBuffer() local entities = { Doors.back, Doors.front, Traps.back, Traps.front, Enemies.default, Enemies.kamikaze, Consu.entities, Attacks.back, Attacks.front, Effects.back, Effects.front, } for i = 1, #entities do for j, v in ipairs(entities[i]) do v:update(j) end end if Map.curMap == 3 then Boss:update() end Joystick.ctrl:update() Player:update() return end -- @U3 if Menu == 2 then TrophyY = TrophyY + TrophyAdd if TrophyY <= 27.0 or TrophyY >= 36.0 then TrophyAdd = -TrophyAdd end Shine:update(0, 0, 10, 10) if click and Trans.timer == 0 then -- options: menu, retry local isBig = (BigBtnMode) and 10 or 0 for x = 68, 145, 77 do if coli.touch(x - isBig, 107 - isBig, x + ((x == 68) and 24 or 30) + isBig, 114 + isBig) then Trans:start((x == 68) and 0 or 1) SaveStatus() if x == 68 then -- menu Sound(1) Sus = math.random(100) else Sound(2) end end end end Coins:update() if Sash.on and Trans.timer < 24.0 then Sash:update() end return end end local function DRAW() cls() -- @D0 if Menu % 2 == 0 then Stars:draw() end -- @D1 if Menu == 0 then local title, gameTitle = nil, { {176, 177, 178, 177, 179, 192, 193, 194, 195}, {208, 209, 210, 211, 224, 225, 226, 227}, {210, 211, 226, 227, 196, 212, 228}, } for i = 1, 2 do title = gameTitle[((Sus == 1 and i == 2) and 3 or i)] plus.title(title, 120, 25 + 20 * (i - 1), 0, 1, 2, 6, nil, nil, nil, true) end if BigBtnTim > 0.0 then local x, y, w, h = 98, 97, 45, 7 if BigBtnMode then x = x - 10 y = y - 10 w = w + 20 h = h + 20 end rectb(x, y, w, h, 3 - math.ceil(BigBtnTim)) end plus.pBorder(TextAnim("START"), 120, 100, TextAnim(), 0, nil, false, 1, false, true) plus.pBorder("(C) DuckAfire", 1, 130, 15, 0, nil, false, 1, true) plus.pBorder("v3.0.2", 220, 130, 15, 0, nil, false, 1, true) -- shadow to main panels if not Joystick.config then for i = 1, 2 do if Panel.online[i] ~= nil and math.abs(Panel.onlineX[i]) == 6 then Trans:shadow() break end end end Panel:draw() if Joystick.config then -- shadow to joystick panel if not Joystick.confPk and not Joystick.info then Trans:shadow() end Joystick:confMenuDraw() if Joystick.confPk then Trans:shadow() -- to "pack conf" panel Joystick:confPackDraw() end end if FirstRun then Trans:shadow() Welcome:draw() end return end -- @D2 if Menu == 1 then map(Map.x, Map.y) for i, v in ipairs(Doors.back) do v:draw() end for i, v in ipairs(Traps.back) do v:draw() end for i, v in ipairs(Attacks.back) do v:draw() end for i, v in ipairs(Effects.back) do v:draw() end Player:draw() for i, v in ipairs(Traps.front) do v:draw() end for i, v in ipairs(Enemies.default) do v:draw() end for i, v in ipairs(Enemies.kamikaze) do v:draw() end for i, v in ipairs(Consu.entities) do v:draw() end for i, v in ipairs(Doors.front) do v:draw() end for i, v in ipairs(Attacks.front) do v:draw() end for i, v in ipairs(Effects.front) do v:draw() end if Map.curMap == 3 then Boss:draw() end Joystick.ctrl:draw() Player:ui() Timer:draw() MatchCountdown() return end -- @D3 if Menu == 2 then Shine:rays() -- winner if Winner then local x, y = 84, TrophyY spr(213, x, y, 4, 3, 0, 0, 3, 2) -- head spr(180, x + 24, y + 68, 4, 3 ) -- base rect(x + 30, y + 48, 12, 20, 0 ) -- body bg rect(x + 33, y + 48, 6, 20, 13 ) -- light Shine:points() else spr(216, 84, TrophyY + 13, 4, 3, 0, 0, 3, 2) end -- options for x = 80, 160, 80 do plus.pBorder(((x == 80) and "Menu" or "Retry"), x, 110, 7, 0, false, nil, 1, false, true) end Coins:draw() -- the 'Sash:draw' is in 'TIC' -- because it must be above the transition if not Winner then Timer:draw(5) end return end end function BOOT() -- UPDATE lbit.boot({"0"}) -- system: update, "welcome memory" lbit.setClass({"system"}) if not lbit.getBool(1, "system", 9) then lbit.clear("memory", true, 1) lbit.setNum(9, 1, "system") end -- MEMORY local clsStat = {"coins", "victories", "defeats", "sequences", "consumables"} local memStat = {} for i = 1, #clsStat do memStat[i] = "0" end -- classes local clsMain = { "config1", "config2", "joystick1", "joystick2", "attack", "achiev1", "achiev2", "skins1", "skins2", "levels", } lbit.boot({ "071211111", "1101", -- config: -- (1) -> music, sound effect, charge, xp, attack, lang, cursor -- (2) -> bigBtn, [joystick], skin, map, theme "040090102", "20211", -- josytick: x, y, radius, opacity, bradius, bopacity, show, mode "200090152", -- attack: x, y, radius, opacity "0", "0", -- achiev "01", "0", -- skins "0", -- levels }, false, 1) lbit.setClass(clsMain, false, 1) lbit.boot( memStat, false, 50) lbit.setClass(clsStat, false, 50) -- COMMON math.randomseed(tstamp()) Achiev[6] = tonumber(lbit.getAll("defeats")) LuckyBox = lbit.getBool(1, "skins1") Trans:start() for y = 1, 15 do Consu.filled[y] = {} end for i = 1, 2 do Volume[i] = lbit.getNum(1 + i - 1, "config1", 2) end -- LANGUAGE Language:updateAll() -- LOAD ELEMENTS AND OBJECTS Stars:boot() Stars:newPos() Joystick:boot() Coup:boot() Player:boot() Shine:boot() Sash:boot() Map:boot() Timer:boot() Boss:boot() Boss:setMethods() -- VARIABLES FirstRun = not lbit.getBool(2, "system") end function TIC() UPDATE() Trans:update() Achiev:panelUpdate() DRAW() Trans:draw() Achiev:panelDraw() Sash:draw() Cursor:draw() end