Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork567
Code examples and snippets
- Copy Sprite Data to a Table
- Load string palette
- Inc and Dec
- Trace all global variables
- Write and read function from memory
- Interpolation functions
- Normally distributed random number generator
- Perlin Noise
- Rescale RangeA to RangeB
- Lapse of time spent at function
- Using the require function to load external code
- Draw ellipse function on Moonscript
- Draw Bézier curves on Moonscript
- Set/Get spritesheet pixel
- Sky gradient
- Clear map
- Palette swapping
- pal function
- palette functions (JavaScript)
- Example Edit the palette using poke
- Rainbow color
- Texrect
- Draw hanging cable between 2 points
- ttri x,y,z Rotation
- Mute one channel of a music track
- Set stereo volume for a channel
- Set sound register volume for a channel
The latest version of TIC-80 supports all of these things with built-in API functions.
- Mouse Scroll
- Peek and Poke Nibbles
peek4andpoke4 - Sprite Flags
fsetandfget - Draw circles
circandcircb
Sometimes you might want to copy sprite data to a table for manipulation, rather than display it on the screen directly. This example shows how to get pixel data for a given sprite by supplying its ID, width and height:
-- Parameters sprite number, width and height of the sprite(s).-- returns a table containing pixel datafunctiongspr(num,w,h)localaddr=0x4000localoffset=w*num%128localhoffset=h*math.floor((w*num)/128)localg= {}forx=0,w-1dog[x]= {}fory=0,h-1dog[x][y]=peek4((addr+ (x//8+y//8*16)*32)*2+x%8+y%8*8)endendreturngend
Useable with palette string defined inpalette page or copied from the Sprite Editor.
-- script: lua--Load palette stringfunctionloadPalette(palette)fori=0,15dor=tonumber(string.sub(palette,i*6+1,i*6+2),16)g=tonumber(string.sub(palette,i*6+3,i*6+4),16)b=tonumber(string.sub(palette,i*6+5,i*6+6),16)poke(0x3FC0+(i*3)+0,r)poke(0x3FC0+(i*3)+1,g)poke(0x3FC0+(i*3)+2,b)endend
// script: jsfunctionloadPalette(string){for(vari=0;i<16;i++){poke(0x03FC0+(i*3),parseInt(string.substr(i*6,2),16));poke(0x03FC0+(i*3)+1,parseInt(string.substr((i*6)+2,2),16));poke(0x03FC0+(i*3)+2,parseInt(string.substr((i*6)+4,2),16));}}
Usesync(4,0,true) to save the editing of the map to cart.
functionclearEntireMap(){for(vary=0;y<136;y++){for(varx=0;x<240;x++){mset(x,y,0);}}}functionclearMap(mx,my){//Since there are 8x8 maps.mx=Math.floor(mx)*30;my=Math.floor(my)*17;for(vary=0;y<17;y++){for(varx=0;x<30;x++){mset(mx+x,my+y,0);}}}
functionclearEntireMap()fory=0,135doforx=0,239domset(x,y,0)endendend
These functions increment and decrement variables by 1. Works only for globals.The use is for minifying the code when you have many somevar = somevar+1 callswhich can waste many characters when names are very descriptive such as enemy_health,while still keeping the code readable.
functioninc(v)_G[v]=_G[v]+1endfunctiondec(v)_G[v]=_G[v]-1end
With MoonScript you can use the+= and-= operators.

-- 'Change screen offset in every scanline' demo-- author: Vadimshake=0d=4functionTIC()ifbtnp()~=0thenshake=30endifshake>0thenpoke(0x3FF9+1,math.random(-d,d))shake=shake-1ifshake==0thenmemset(0x3FF9,0,2)endendcls(12)print("PRESS ANY KEY TO GLITCH!",54,64)endfunctionBDR(row)ifshake>0thenpoke(0x3FF9,math.random(-d,d))endend
-- swap c0 and c1 colors, call pal() to resetfunctionpal(c0,c1)if(c0==nilandc1==nil)thenfori=0,15dopoke4(0x3FF0*2+i,i)endelsepoke4(0x3FF0*2+c0,c1)endend
// script: jsfunctionpal(c0,c1){if(arguments.length==2)poke4(0x3ff0*2+c0,c1)elsefor(vari=0;i<16;i++)poke4(0x3ff0*2+i,i)}
Slightly different approach in Janet which uses a macro and can handle any number of swaps.
(defmacrowith-pallete-swap [pallete&body]""" Swap the color pallete during the given body. Pallete is a dict of {from to} color ids.""" (with-syms [$from$to] ~(do# apply the theme (loop [[,$from ,$to]:pairs ,pallete] (tic80/poke4 (+ (*0x3FF02) ,$from) ,$to)) ,;body# remove theme (loop [[,$from ,$to]:pairs ,pallete] (tic80/poke4 (+ (*0x3FF02) ,$from) ,$from)))))(with-pallete-swap {0123} (tic80/map0088))
-- title: screen wave and sky gradient snippet-- author: Darkhog-- TIC function cut because it doesn't matter here.-- This code can be used for e.g. water or some hazy/hot environments.wavelimit=136/2functionscanline(row)-- skygradientpoke(0x3fc0,190-row)poke(0x3fc1,140-row)poke(0x3fc2,0)-- screen waveifrow>wavelimitthenpoke(0x3ff9,math.sin((time()/200+row/5))*10)elsepoke(0x3ff9,0)endend
--sets the palette indice i to specified rgb--or return the colors if no rgb values are declared.functionpal(i,r,g,b)--sanity checksifi<0theni=0endifi>15theni=15end--returning color r,g,b of the colorifr==nilandg==nilandb==nilthenreturnpeek(0x3fc0+(i*3)),peek(0x3fc0+(i*3)+1),peek(0x3fc0+(i*3)+2)elseifr==nilorr<0thenr=0endifg==nilorg<0theng=0endifb==nilorb<0thenb=0endifr>255thenr=255endifg>255theng=255endifb>255thenb=255endpoke(0x3fc0+(i*3)+2,b)poke(0x3fc0+(i*3)+1,g)poke(0x3fc0+(i*3),r)endend
pal=(i=0, r, g, b)->mx=math.maxmn=math.minfl=math.floori= fl(mx(0, mn(15, i)))if r==niland g==niland b==nilreturn peek(0x3fc0+(i*3)), peek(0x3fc0+(i*3)+1), peek(0x3fc0+(i*3)+2)r= fl(mx(0, mn(255, ror0)))g= fl(mx(0, mn(255, gor0)))b= fl(mx(0, mn(255, bor0)))poke(0x3fc0+(i*3), r)poke(0x3fc0+(i*3)+1, g)poke(0x3fc0+(i*3)+2, b)
functionpal(i,r,g,b){i=(i|0)*3;if(r===undefined&&g===undefined&&b===undefined){return[peek(16320+i),peek(16321+i),peek(16322+i)];}else{poke(16320+i,r);poke(16321+i,g);poke(16322+i,b);}}
-- 'shake screen demo' demo-- author: Vadimshake=0d=4functionTIC()ifbtnp()~=0thenshake=30endifshake>0thenpoke(0x3FF9,math.random(-d,d))poke(0x3FF9+1,math.random(-d,d))shake=shake-1ifshake==0thenmemset(0x3FF9,0,2)endendcls(12)print("PRESS ANY KEY TO SHAKE THE SCREEN!",24,64)end
-- title: Trace all global variables-- author: Al Rado 28.02.2017-- desc: Standard Lua demo-- script: lua-- input: gamepad-- pal: DB16localseen={}functiondump(t,i)seen[t]=truelocals={}localn=0forkinpairs(t)don=n+1s[n]=kendtable.sort(s)fork,vinipairs(s)dotrace(i..v)v=t[v]iftype(v)=="table"andnotseen[v]thendump(v,i.."\t")endendendcls()trace("---------------------")trace("All global variables:")dump(_G,"")trace("---------------------")print("See all global variables in console!")functionTIC()exit()end
Update:fset andfget are now built-in API functions and the Sprite Editor also supports setting flags during editing.
FLAGS={}functionfset(i,f,v)localval=FLAGS[i]or0localmask=1<<fFLAGS[i]=vandval|maskorval&~maskendfunctionfget(i,f)localval=FLAGS[i]or0returnval&(1<<f)~=0endfset(5,5,true)trace(fget(5,5))fset(5,5,false)trace(fget(5,5))cls()functionTIC()end
The spritesheet isn't a 128x256 image in the memory, it's 512 consecutive 8x8 4 bit sprites.These functions map it to a 128x256 image.
-- set spritesheet pixelfunctionsset(x,y,c)localaddr=0x4000+(x//8+y//8*16)*32-- get sprite addresspoke4(addr*2+x%8+y%8*8,c)-- set sprite pixelend
-- get spritesheet pixelfunctionsget(x,y)localaddr=0x4000+(x//8+y//8*16)*32-- get sprite addressreturnpeek4(addr*2+x%8+y%8*8)-- get sprite pixelend
functionsget(x,y){returnpeek4(32768+56*(x/8|0)+960*(y/8|0)+x+8*(y|0));}functionsset(x,y,c){poke4(32768+56*(x/8|0)+960*(y/8|0)+x+8*(y|0),c);}
;parlortricks;script:fennel(fnsget [xy]"get sprite sheet pixel" (localaddr (+0x4000 (* (+ (//x8) (* (//y8)16))32))) (peek4 (+ (*addr2) (%x8) (* (%y8)8))))
;parlortricks;script:fennel(fnsset [xyc]"set sprite sheet pixel" (localaddr (+0x4000 (* (+ (//x8) (* (//y8)16))32))) (poke4 (+ (*addr2) (%x8) (* (%y8)8))c))
made byRaidez
function_360(x,y,r,color,fill)ifnotfillthenfora=1,360,1do--draw outline onlydx=math.cos(a)*r+xdy=math.sin(a)*r+ypix(dx,dy,color)endelseforr=r,1,-1do--draw circle for each radius (for fill circle)fora=1,360,1dodx=math.cos(a)*r+xdy=math.sin(a)*r+ypix(dx,dy,color)endendendendfunction_bresenham(x,y,r,color)px=0py=rm=5-4*rwhile(px<=py)dopix((px+x),(py+y),color)pix((py+x),(px+y),color)pix((-px+x),(py+y),color)pix((-py+x),(px+y),color)pix((px+x),(-py+y),color)pix((py+x),(-px+y),color)pix((-px+x),(-py+y),color)pix((-py+x),(-px+y),color)ifm>0thenpy=py-1m=m-8*pyendpx=px+1m=m+8*px+4endendfunction_andres(x,y,r,color,fill)px=xpy=yd=r-1a=r--a=r-1b=0while(a>=b)doifnotfillthenpix((px+b),(py+a),color)pix((px+a),(py+b),color)pix((px-b),(py+a),color)pix((px-a),(py+b),color)pix((px+b),(py-a),color)pix((px+a),(py-b),color)pix((px-b),(py-a),color)pix((px-a),(py-b),color)elseline((px+b),(py-a),(px+b),(py+a),color)line((px+a),(py-b),(px+a),(py+b),color)line((px-b),(py-a),(px-b),(py+a),color)line((px-a),(py-b),(px-a),(py+b),color)endifd>=2*bthend=d-2*b-1b=b+1elseifd<2*(r-a)thend=d+2*a-1a=a-1elsed=d+2*(a-b-1)a=a-1b=b+1endendendfunction_axis(cx,cy,r,color,fill)ifnotfillthenfory=-r,r,1do--outlineforx=-r,r,1doif (x*x+y*y)<=(r*r)thenpix(cx+x,cy+y,color)endendendr=r-1fory=-r,r,1do--unfillforx=-r,r,1doif (x*x+y*y)<=(r*r)thenpix(cx+x,cy+y,-1)endendendelsefory=-r,r,1doforx=-r,r,1doif (x*x+y*y)<=(r*r)thenpix(cx+x,cy+y,color)endendendendendfunctioncirc(x,y,r,color,algo)ifalgo=="360"then--don't use it, it's false/incomplete--_360(x,y,r,color,true)elseifalgo=="bresenham"then_bresenham(x,y,r,color)_andres(x,y,(r),color,true)elseifalgo=="andres"then_andres(x,y,r,color,true)elseifalgo=="axis"then_axis(x,y,r,color,true)endendfunctioncircb(x,y,r,color,algo)ifalgo=="360"then--don't use it, it's false/incomplete--_360(x,y,r,color,false)elseifalgo=="bresenham"then_bresenham(x,y,r,color)elseifalgo=="andres"then_andres(x,y,r,color,false)elseifalgo=="axis"then_axis(x,y,r,color,false)endendfunctionTIC()cls()circb(100,60,40,14,"bresenham")end
made byharraps
-- script: moon--function to draw an ellipse--x,y : position of the ellipse--A,B : major and minor radius--ang : angle of the ellipse in radian--col : color of the filled ellipseell=(x,y,A,B,ang,col)->--angles usedcos=math.cos angsin=math.sin angcos2=math.cos ang+math.pi*0.5sin2=math.sin ang+math.pi*0.5--vectors in euclidian spaceux=A*cosuy=A*sinvx=B*cos2vy=B*sin2--bounds of ellipsew=math.sqrt ux*ux+ vx*vxh=math.sqrt uy*uy+ vy*vy--rect x-w+1,y-h+1,w*2,h*2,8--for each pixel within the boundsfor x0=-w,wfor y0=-h,hx1=( cos*x0+ sin*y0)/Ay1=(-sin*x0+ cos*y0)/Bif x1*x1+ y1*y1<1pix x+x0,y+y0,col
made byharraps
-- title: bezier-- desc: function to draw Bezier curves-- script: moon-- input: gamepadclassPointnew:(x,y)=>@x=x@y=yBezier=-- simple linelinear:(t,p0,p1)->return{x:p0.x+t*(p1.x-p0.x)y:p0.y+t*(p1.y-p0.y)--z:p0.z+t*(p1.z-p0.z)}-- simple curvequadratic:(t,p0,p1,p2)->n=1-tt0=n*nt1=n*t*2t2=t*treturn{x:t0*p0.x+t1*p1.x+t2*p2.xy:t0*p0.y+t1*p1.y+t2*p2.y--z:t0*p0.z+t1*p1.z+t2*p2.z}--better curvecubic:(t,p0,p1,p2,p3)->n=1-tt0=n*n*nt1=3*n*n*tt2=3*n*t*tt3=t*t*treturn{x:t0*p0.x+t1*p1.x+t2*p2.x+t3*p3.xy:t0*p0.y+t1*p1.y+t2*p2.y+t3*p3.y--z:t0*p0.z+t1*p1.z+t2*p2.z+t3*p3.z}classLinearnew:(col,seg,x0,y0,x1,y1)=>@col=col@seg=seg@p0=Point x0,y0@p1=Point x1,y1draw:=>t=0pad=1/@segpp=@p0for i=1,@segt+=padnp=Bezier.linear t,@p0,@p1line pp.x,pp.y,np.x,np.y,@colpp=npline pp.x,pp.y,@p1.x,@p1.y,@colclassQuadraticnew:(col,seg,x0,y0,x1,y1,x2,y2)=>@col=col@seg=seg@p0=Point x0,y0@p1=Point x1,y1@p2=Point x2,y2draw:=>t=0pad=1/@segpp=@p0for i=1,@segt+=padnp=Bezier.quadratic t,@p0,@p1,@p2line pp.x,pp.y,np.x,np.y,@colpp=npline pp.x,pp.y,@p2.x,@p2.y,@colclassCubicnew:(col,seg,x0,y0,x1,y1,x2,y2,x3,y3)=>@col=col@seg=seg@p0=Point x0,y0@p1=Point x1,y1@p2=Point x2,y2@p3=Point x3,y3draw:=>t=0pad=1/@segpp=@p0for i=1,@segt+=padnp=Bezier.cubic t,@p0,@p1,@p2,@p3line pp.x,pp.y,np.x,np.y,@colpp=npline pp.x,pp.y,@p3.x,@p3.y,@colc=Cubic6,60,0,0,246,0,0,136,246,136cls1c\draw!exportTIC=->
Update:peek4 andpoke4 are now built-in API functions.
// script: jsfunctionpeek4(address){varvalue=peek(address>>1);returnaddress&1?value>>4:value&0x0f;}functionpoke4(address,value){vartmp=peek(address>>1);value&=0x0f;poke(address>>1,address&1?(tmp&0x0f)|(value<<4):(tmp&0xf0)|value);}
-- script: luafunctionpeek4(address)localvalue=peek(address//2)if(address&1)thenreturnvalue>>4elsereturnvalue&0x0fendendfunctionpoke4(address,value)localtmp=peek(address//2)value=value&0x0fif(address&1)thentmp=(tmp&0x0f)|(value<<4)elsetmp=(tmp&0xf0)|valueendpoke(address//2,tmp)end
made byharraps
-- script: moon-- title: brainfuck interpreter-- author: by oSchyns-- version 0.3.0BF="++++ ++++".."[>+++++ +++++<-]"..">++++.----- ----- -.--- ---."--..--uncomment to test error output--"<< force a tape outbound"IN=""--tape to perform computationclassTapenew:(l,m)=>@l=l--cell limit@m=m--loop the finite tape@t={}--tape@h=0--read head--correct values@l-=1if@l~=nil@m-=1if@m~=nil--rhead out of the bounds of the tapeunbound:=>if@m~=nilthenreturn@h>@m@h<0--init nil cell to zeroinit:=>@t[@h]=0if@t[@h]==nil--move read head to the leftmvL:=>@h-=1if@m~=nilthenif@h<0@h=@m--move read head to the rightmvR:=>@h+=1if@m~=nilthenif@h>@m@h=0--decreament celldecr:=>@init!@t[@h]-=1if@l~=nilthenif@t[@h]<0@t[@h]=@l--increament cellincr:=>@init!@t[@h]+=1if@l~=nilthenif@t[@h]>@l@t[@h]=0--ins: insert value in cell--out: get value from cellins:(i)=>@t[@h]=iif i~=nilout:=>@t[@h]or0--stack to hold indexes of bracketsclassStacknew:=>@s={}top:=>@s[#@s]pop:=>@s[#@s]=nilpush:(i)=>@s[#@s+1]=i--automaton to execute the programclassAutomatonnew:(p,i,l,m)=>@t=Tape l,m--tape@s=Stack!--stack@r=1--read head@p=""--program@i=i--input@o=""--output@e=nil--error--strip useless chars of programfor cin p\gmatch"[<>%+%-%[%]%.%,]+"@p..=c b=@check!if b~=nil@e="brackets mismatch at"..b--check for brackets mismatchcheck:=> s=Stack!for i=1,#@pif@program(i)=='['then s\push ielseif@program(i)==']'return iif#s.s<=0 s\pop!return s\top!if#s.s>0returnnil--get char from inputinput:=>if@i==nilor@i==""thenreturnnil c1=@i\byte!--1st char of i@i=@i\sub2--remove 1st charreturn c1--output: add char to output--program: get instruction at 'n'--continue: continue executionoutput:(n)=>@o..=string.char nprogram:(n)=>@p\sub n,ncontinue:=>@r<=#@pand@e==nil--find matching bracketmatch:(b)=> m=1while m>0and b<=#@p b+=1if@program(b)=='['then m+=1elseif@program(b)==']'then m-=1return b--opening bracketopen:=>--jump to matching bracketif(@t\out!)==0then@r=@match@relse@s\push@r--closing bracketclos:=>if(@t\out!)==0then@s\pop!elseif@s\top!~=nilthen@r=@s\top!--automaton's' execution stepstep:=>switch@program@rwhen'<'then@t\mvL!when'>'then@t\mvR!when'-'then@t\decr!when'+'then@t\incr!when','then@t\ins@input!when'.'then@output@t\out!when'['then@open!when']'then@clos!@r+=1if@t\unbound!@e="tape outbound !"--display to see the executionclassDisplaynew:(a,s=1,f=nil)=>@a=a--automaton@s=s/60--number of update per second@c=0--counter@f=3--display format of cellsif f=="hexadecimal"then@f=2elseif f=="character"then@f=1--execute automaton all at onceexecute:=>while@a\continue!@a\step!@output!--update automaton at given frame rateupdate:=>if@a\continue!if@c<1then@c+=@selse@c=0@a\step!@draw!--draw automaton state and outputdraw:=> cls!@tape8,2@program8,12@output4--draw the tapetape:(w=6,h=0,c=1,a=3)=> rect0,h,240,w,c l=math.ceil(40/@f)*0.5 p=(w-6)*0.5 q=6*@f b=l-@a.t.h d=15if@f==2then d=6elseif@f==1then d=3 rect l*q-d,h,6*@f,w,afor i=@a.t.h-l,@a.t.h+lunless i<0 c=15 c=14if i==@a.t.h v=@a.t.t[i]or0if@f==3then v="%3d"\format velseif@f==2then v="%2x"\format velseif@f==1then v=string.char vprint v,(i+b)*q-d,h+p,c--draw the programprogram:(w=6,h=6,c=1,a=3)=> rect0,h,240,w,c rect117,h,6,w,a p=(w-6)*0.5 b=20-@a.rfor i=@a.r-20,@a.r+20unless i<1 c=15 c=14if i==@a.r v=@a\program iprint v,(i+b)*6-2,h+p,c--print result of executionoutput:(s=0)=> i=s o=@a.o.."\n"--print each linefor lin o\gmatch"[%S\t]*\n"print l,0,i*6 i+=1breakif i==21--no more room--print errorunless@a.e==nilprint@a.e,0,130,6--create automaton 'TM' using--program : BF--input : INTM=AutomatonBF,IN,128,nil--create display 'DP' using--automaton : TM--frame rate : 30--cell display : decimalDP=DisplayTM,30,"decimal"--execute the automaton and display itexportTIC=->DP\update!--http://moonscript.org/reference/--http://moonscript.org/compiler/--https://esolangs.org/wiki/Brainfuck--http://zacstewart.com/2013/09/15/learning-cpp-a-brainfuck-interpreter.html
-- title: Write function to memory-- author: Al Rado-- desc: Shows how write code to memory-- script: lua-- addres to write code block / sprites addresADDR=0x4000-- target function, for examplefunctionprints(msg,x,y,c)fori=-1,1doforj=-1,1doprint(msg,x+i,y+j,0);endendprint(msg,x,y,c);end-- write dump of target function to memorylocalfuncBin=string.dump(prints,true)fori=1,#funcBindolocalcode=funcBin:byte(i)poke(ADDR+i-1,code)endsync(0,0,true)-- trace lenght of memory blocktrace("len:"..#funcBin)exit()functionTIC()end
After the function is written to the cartridge memory, it can be read and run.It is important to specify the length of the code block (LEN) shown in the previous example
-- title: Read function from memory-- author: Al Rado-- desc: Shows how read code from memory-- script: lua-- addres of code block / sprites addresADDR=0x4000-- lenght of memory block in bytesLEN=211-- get dump of function from memorylocalfuncStr=""fori=1,LENdolocalcode=peek(ADDR+i-1)funcStr=funcStr..string.char(code)end-- load function from stringlocalprints=assert(load(funcStr));-- call target functioncls(3)prints("HELLO WORLD!",84,84,15)functionTIC()end
The 6-bit structure of the music tracks makes muting a single channel tricky. A channel consists of 16 patterns, and one 6-bit pattern is always span across two nibbles (a half-byte) that are from separate bytes. The other nibble also contains data from some other pattern, which should be left intact. To mute all patterns of the channel in a song, some bitmasking is needed.
-- title: Mute one channel of a music track-- author: @oggborbis-- script: luafunctionmutechannel(track,channel)--tracks 0-8, channels 1-4localoffset=3*(channel+channel%2)/2-2-channel%2localmaskifchannel%2==0thenmask=0x3--the high nibble is bitmasked empty for channels 2&4elsemask=0xc--the low nibble is bitmasked empty for channels 1&3endfori=0,15do--there are 16 sections in a track, each one taking 3 bytes for four 6-bit patternslocaladr=(0x13e64+51*track+3*i)*2--a track is 51 bytes longlocalq=adr+offset+channel%2poke4(q,peek4(q)&mask)--one half of this nibble (2bit) is bitmasked emptypoke4(adr+offset+1-channel%2,0)--the other nibble (4bit) can be emptiedendend
You can set a channels global volume for both the left and right side. Check outthe RAM mapping here
(defnset-stereo-volume [channelleft-volume&optright-volume]""" Set stereo volume (0 - 15) for a channel (0 - 3). If one volume provided, will use for both left and right.""" (defaultright-volumeleft-volume)# NOTE: need to multiply the hex by 2 to convert from byte to nibble space. (tic80/poke4 (*2 (+0x14000channel))left-volume) (tic80/poke4 (+1 (*2 (+0x14000channel)))right-volume))
You can also update the volume of channel in the sound register, which happens independent of the sfx() volume parameter and Mxy commands in the tracker. Here's a function that lets you set the volume as a percentage of its current value.
(defnset-channel-register-volume [channelvolume-percent]""" Set the volume percentage (0 to 1) for a channel (from 0 to 3) in the sound register. This effects the SFX volume envelope, and is independent of the sfx() volume parameter and Mxy commands in the tracker. for more --> https://github.com/nesbox/TIC-80/wiki/RAM#sound-registers""" (let [mem-location (+3 (*2 (+0x0FF9C (*18channel))))volume (tic80/peek4mem-location)new-volume (math/round (*volumevolume-percent))] (tic80/poke4mem-locationnew-volume)))
Go to page hereRGB Address Page
This RGB color(Red Green Blue) is mixed colors. Can make fade in with color.Be simple code example. Easy to use animate colors.
-- title: RGB color animation test-- author: masternama-- script: luasw=0-- screen waitfunctionTIC()ifsw>0thencls()-- black colorpoke(0x3FC0,255)-- Redendifsw>10thencls()-- black colorpoke(0x3FC1,255)-- Greenendifsw>20thencls()-- black colorpoke(0x3FC2,255)-- Blueendifsw>30thenreset()-- Loop resetendprint("HELLO WORLD!",88,70)--sync(0,0,true)sw=sw+1end
Some useful JavaScript 16-color palette functions for the console in your browser:
// author: Anastasia Dunbarfunctionreverse(x){returntypeofx==="string"?x.split("").reverse().join(""):x.reverse();}functionchunkString(string,n){varchunks=[];for(vari=0;i<string.length;i+=n){chunks.push(string.substring(i,i+n));}returnchunks;}functionhexChars(string){returnstring.replace(/[^0-9A-F]/gi,"");}functionhexTo24RGB(string){returnchunkString(hexChars(string).substring(0,6),2).map(x=>parseInt(x,16));}functionperceivedBrightness(R,G,B){//Doesn't matter what range R, G and B is within.returnMath.sqrt((.299*(R**2))+(.587*(G**2))+(.114*(B**2)));}functionsortPaletteByBrightness(array){//From dark to bright.array=array.map(x=>{if(typeofx==="string"){returnhexTo24RGB(x);}returnx;});returnarray.sort((x,y)=>perceivedBrightness(...x)-perceivedBrightness(...y));}functionrgbArrayToHex(array){returnarray.flat().map(x=>x.toString(16).padStart(2,"0")).join("");}functionhexToRgbArray(string){returnchunkString(hexChars(string),6).map(x=>chunkString(x,2).map(y=>parseInt(y,16)));}functionmakeSortedPalette(string){returnrgbArrayToHex(sortPaletteByBrightness(string.split(/\s+/).filter(x=>x)));}functionvisualizePalette(string){//Normal TIC-80 palette string as a parameter.chunkString(string,6).forEach((x,i)=>{console.log(`${String(i).padStart(2)}: %c${x}`,`background-color:#${x};color:#${perceivedBrightness(...hexTo24RGB(x))<100?"fff":"000"}`);});}
Update: Mouse scroll can now be retrieved easily via themouse API function.
Returns mouse Y-scroll as an integer from -32 to +31
functionmscroll()localscroll=(peek(0x0FF87)&0x7E)>>1return-(scroll&32)+(scroll&31)end
Linear interpolation
functionlerp(a,b,mu)returna*(1-mu)+b*muend
Cosine interpolation
functioncoserp(a,b,mu)localT=(1-math.cos(mu*math.pi))/2returna*(1-T)+b*Tend
Cubic interpolation
functioncuberp(a,b,c,d,t)localA=d-c-a+blocalB=a-b-AlocalC=c-alocalD=blocalT=t*treturnA*t*T+B*T+C*t+Dend
Return a random number in a pseudo-normal distribution with a mean of m and d as standard deviation (with a "quality" of q)
functionrandomnorm(m,d,q)ifm==nilthenm=0endifd==nilthend=1endifq==nilthenq=10endlocalsum=0fori=1,q*2dosum=sum+(math.random())endval=(sum-q)*d+mifval%1>0.5thenval=math.ceil(val)endifval%1<0.5thenval=math.floor(val)endreturnvalend
Sky gradient it works by usingSCN(). Page:Sky gradient Page
-- title: Sky Gradient-- author: masternama-- script: lua--[[Color Gradient looks like e.g(sky, lava, water & shadow)Result will be smooth gradient...]]functionSCN(scn)--poke(0x3fc0,scn) -- Unused Sky Colorpoke(0x3fc1,scn)poke(0x3fc2,scn+120)endfunctionTIC()cls()end
red=255grn=0blu=0cyRG=truecyGB=falsecyBR=falsefunctionrainbowColor(slot){if(cyRG==true){grn++if(grn>255){grn=255red--}if(red==0){cyRG=falsecyGB=true}}if(cyGB==true){blu++if(blu>255){blu=255grn--if(grn==0){cyGB=falsecyBR=true}}}if(cyBR==true){red++if(red>255){red=255blu--if(blu==0){cyBR=falsecyRG=true}}}if(red==255){}poke(0x3fc0+(slot*3)+2,blu)poke(0x3fc0+(slot*3)+1,grn)poke(0x3fc0+(slot*3),red)}
Sometimes you come across wanting to map/scale a number from one range to another. For example you have a numer from the range [0-10] and wants to scale it to [100-1000]. Using the rescale-linear function you could do the following:
(fn_G.TIC [] (cls) (print (rescale-linear90101001000)))
The value 9 would end up being 910.0
;parlortricks;script:fennel(fnrescale-linear [valuestart1stop1start2stop2]"remap rangeA to rangeB for a given value" (+start2 (* (-stop2start2) (/ (-valuestart1) (-stop1start1)))))
-- script: moon-- author: darltrashlapse=(fn,...)->t= time()fn(...)time()- texportTIC=->cls6spent= lapse->for x=0,240/8for y=0,136/8cx=math.sin(x*4+time()/300)*5cy=math.cos(y*4+time()/300)*5circ cx+x*8, cy+y*8,3,7print"Took: #{spent}ms",10,10,12
-- script: lua-- author: darltrashlocalfunctionlapse(fn, ...)localt=time()fn(...)returntime()-tendfunctionTIC()cls(6)localspent=lapse(function()forx=0,240/8dofory=0,136/8dolocalcx,cycx=math.sin(x*4+time()/300)*5cy=math.cos(y*4+time()/300)*5circ(cx+x*8,cy+y*8,3,7)endendend)print("Took:"..spent.."ms",10,10,12)end
Draws a textured rectangle.
functiontexrect(x1,y1,x2,y2,u1,v1,u2,v2,usemap,colorkey)usemap=usemaporfalsecolorkey=colorkeyor-1textri(x1,y1,x1,y2,x2,y2,u1,v1,u1,v2,u2,v2,usemap,colorkey)textri(x1,y1,x2,y1,x2,y2,u1,v1,u2,v1,u2,v2,usemap,colorkey)end
Similar function in Janet, but uses width and height values.
(defntrect [xywhuxuyuwuh&optuse-maptrans]"Draw a textured rectangle using 2 textured tiangles" (defaultuse-mapfalse) (defaulttrans-1) (let [[x1y1] [xy] [x2y2] [(+xw)y] [x3y3] [x (+yh)] [x4y4] [(+xw) (+yh)] [u1v1] [uxuy] [u2v2] [(+uxuw)uy] [u3v3] [ux (+uyuh)] [u4v4] [(+uxuw) (+uyuh)]] (tic80/ttrix1y1x2y2x3y3u1v1u2v2u3v3use-maptrans) (tic80/ttrix2y2x3y3x4y4u2v2u3v3u4v4use-maptrans)))

-- script: lua-- author: ArchaicVirus-- description: Draws a 'hanging' cable between two pointsfunctiondraw_cable(p1,p2,color)-- calculate the length and midpoint of the cablelocaldx=p2.x-p1.xlocaldy=p2.y-p1.ylocallength=math.sqrt(dx*dx+dy*dy)localmidpoint= {x= (p1.x+p2.x)/2,y= (p1.y+p2.y)/2 }-- calculate the height of the saglocalsag_height=math.max(length/8,8)-- draw the cableforx=0,lengthdolocaly=math.sin(x/length*math.pi)*sag_heightlocalpx=p1.x+dx*x/length+0.5localpy=p1.y+dy*x/length+y+0.5ifx>0thenlocalpx_prev=p1.x+dx* (x-1)/length+0.5localpy_prev=p1.y+dy* (x-1)/length+math.sin((x-1)/length*math.pi)*sag_height+0.5line(px_prev,py_prev,px,py,color)elsepix(px,py,color)endendend

-- title: Sprite Rotation-- author: Skeptim with ChatGPT-- desc: Sprite rotation along x,y,z axes.-- site: https://tic80.com/dev?id=6947-- license: GNU General Public License v3.0 (GPL)-- version: 2.0-- script: lua-- input: gamepadlocalsin=math.sinlocalcos=math.cos-- Initialize variableslocalx,y,z=120,68,0-- Positionlocalangx,angy,angz=0,0,0-- Rotation angles-- UV coordinates for each cornerlocalu1,v1=8,0-- Corner 1, top leftlocalu2,v2=24,0-- Corner 2, top rightlocalu3,v3=24,16-- Corner 3, bottom rightlocalu4,v4=8,16-- Corner 4, bottom left-- Rotation center: can be the center of the sprite or notlocalu0=(u1+u3)/2-- U coordinatelocalv0=(v1+v3)/2-- V coordinate-- Scale factorlocalscale=4localfunctionsprite_rotation(x,y,z,angx,angy,angz,u1,v1,u2,v2,u3,v3,u4,v4,scale,chromakey,u0,v0)localz_offset=300-- to avoid negative z values-- Calculate rotation matriceslocalsinX,cosX=sin(angx),cos(angx)localsinY,cosY=sin(angy),cos(angy)localsinZ,cosZ=sin(angz),cos(angz)-- Center of the spriteifnotu0ornotv0thenu0=(u1+u3)/2v0=(v1+v3)/2end-- Define the vertices of the first trianglelocalvertices1= { {x= (u1-u0)*scale,y= (v1-v0)*scale,z=0 }, {x= (u2-u0)*scale,y= (v2-v0)*scale,z=0 }, {x= (u4-u0)*scale,y= (v4-v0)*scale,z=0 } }-- Apply rotation matrices to the vertices of the first trianglefor_,vertexinipairs(vertices1)dolocalx,y,z=vertex.x,vertex.y,vertex.z-- Apply rotation around x-axislocalnewY=y*cosX-z*sinXlocalnewZ=y*sinX+z*cosXy,z=newY,newZ-- Apply rotation around y-axislocalnewX=x*cosY+z*sinYnewZ=-x*sinY+z*cosYx,z=newX,newZ-- Apply rotation around z-axisnewX=x*cosZ-y*sinZnewY=x*sinZ+y*cosZx,y=newX,newYvertex.x,vertex.y,vertex.z=x,y,z+z_offsetend-- Define the vertices of the first trianglelocalvertices2= { {x= (u2-u0)*scale,y= (v2-v0)*scale,z=0 }, {x= (u4-u0)*scale,y= (v4-v0)*scale,z=0 }, {x= (u3-u0)*scale,y= (v3-v0)*scale,z=0 } }-- Apply rotation matrices to the vertices of the second trianglefor_,vertexinipairs(vertices2)dolocalx,y,z=vertex.x,vertex.y,vertex.z-- Apply rotation around x-axislocalnewY=y*cosX-z*sinXlocalnewZ=y*sinX+z*cosXy,z=newY,newZ-- Apply rotation around y-axislocalnewX=x*cosY+z*sinYnewZ=-x*sinY+z*cosYx,z=newX,newZ-- Apply rotation around z-axisnewX=x*cosZ-y*sinZnewY=x*sinZ+y*cosZx,y=newX,newYvertex.x,vertex.y,vertex.z=x,y,z+z_offsetend-- Draw the rotated triangles to form a sprite using ttrilocalv1_1,v2_1,v3_1=vertices1[1],vertices1[2],vertices1[3]localv1_2,v2_2,v3_2=vertices2[1],vertices2[2],vertices2[3]-- Draw first trianglettri(v1_1.x+x,v1_1.y+y,v2_1.x+x,v2_1.y+y,v3_1.x+x,v3_1.y+y,u1,v1,u2,v2,u4,v4,0,chromakey,v1_1.z,v2_1.z,v3_1.z )-- Draw second trianglettri(v1_2.x+x,v1_2.y+y,v2_2.x+x,v2_2.y+y,v3_2.x+x,v3_2.y+y,u2,v2,u4,v4,u3,v3,0,chromakey,v1_2.z,v2_2.z,v3_2.z )endfunctionTIC()cls(1)sprite_rotation(x,y,z,angx,angy,angz,u1,v1,u2,v2,u3,v3,u4,v4,scale,14,u0,v0)-- Update rotation angles based on inputifbtn(0)thenangx=angx+0.1endifbtn(1)thenangy=angy+0.1endifbtn(2)thenangz=angz+0.1endifbtn(3)thenu0=u0+0.2endend
Using co-routines we can make a console simulation, accepting user input.By @dcaillia
-- example imperative console programfunctionprogram()console.print("Hoeveel spelers?")localcount=console.read()fori=1,countdolocalnamewhiletruedoconsole.newline()console.print("Speler"..i.."?")name=console.read()if (string.len(name)<2)thenconsole.print("(minstens 2 letters)")elsebreakendendconsole.print("Dag"..name.."!")console.newline()endend-- library functions below: just copy this partlocalmain=coroutine.create(function()cls()program()-- when program is done, stick herecoroutine.yield(wait,"wait")end)localwait=coroutine.create(function()-- never actually resumedend)localinput=coroutine.create(function()whiletruedoconsole.newline()console.input=truecoroutine.yield(wait,"wait")endend)localr,n=main,"main"functionTIC()while (not (n=="wait"))dos,r,n=coroutine.resume(r)trace(n)endr,n=console.run()endconsole= {x=0,y=0,h=6,-- row height in pixelsr=120//6,-- number of rowsline="",input=false,log={}}functionconsole.run()if (console.input)thenlocalc=getc()if (c)thenrect(console.x,console.y,6,6,0)if (c=="enter")thenconsole.input=falsereturnmain,"main"elseifc=="backspace"thenlocaldel=console.line:sub(#console.line,#console.line)localwidth=print(del,0,200)console.x=console.x-widthrect(console.x,console.y,width,6,0)console.line=console.line:sub(1,#console.line-1)elseconsole.print(c)end--console.status(console.x .. "," .. console.y .. " " .. console.line)endlocalcolor=6rect(console.x,console.y,6,6,color)endreturnwait,"wait"endfunctionconsole.newline()table.insert(console.log,console.line)console.line=""console.x=0console.y=console.y+console.hif (console.y>=console.h*console.r)thenrect(0,0,240,console.h*console.r,0)localoffset=#console.log-console.r+2fori=1,console.r-2doprint(console.log[i+offset],0,i*console.h)endconsole.y=console.h*(console.r-1)endendfunctionconsole.print(c)console.line=console.line..clocalwidth=print(c,console.x,console.y)console.x=console.x+widthendfunctionconsole.read()coroutine.yield(input,"input")localline=console.lineconsole.newline()returnlineendfunctionconsole.status(text)rect(0,120,240,128,1)print(text,0,120)print(#console.log,0,128)endkeys= {enter=50,backspace=51,shift=64,capslock=62,lbracket=39,rbracket=40,comma=45,period=46,space=48,tab=49,equals=38,ctrl=63}functiongetc()localletter=nil-- loop to handle the easy cases: A to Z, 0 to 9fori=1,36doif(keyp(i))thenifi<=26then-- letterletter=string.char(i+97-1)-- 65 is `a` in asciielse-- numberletter=string.char(i-26+48-1)-- 48 is `0` in asciiendreturnletterendendif(keyp(keys.space))thenletter=""elseif(keyp(keys.tab))thenletter=""elseif(keyp(keys.comma))thenletter=","elseif(keyp(keys.period))thenletter="."elseif(keyp(keys.lbracket))thenletter="["elseif(keyp(keys.rbracket))thenletter="]"elseif(keyp(keys.equals))thenletter="="elseif(keyp(keys.enter))thenletter="enter"elseif(keyp(keys.backspace))thenletter="backspace"endreturnletterend
TIC-80 tiny computerhttps://tic80.com |Twitter |Telegram |Terms
Built-in Editors
Console
Platform
RAM &VRAM |Display |Palette |Bits per Pixel (BPP) |
.ticFormat |Supported Languages
Other
API
- BDR (0.90)
- BOOT (1.0)
- MENU
- OVR (deprecated)
- SCN (deprecated)
- TIC
- btn &btnp
- circ &circb
- clip
- cls
- elli &ellib (0.90)
- exit
- fget &fset (0.80)
- font
- key &keyp
- line
- map
- memcpy &memset
- mget &mset
- mouse
- music
- peek,peek4
- peek1,peek2 (1.0)
- pix
- pmem
- poke,poke4
- poke1,poke2 (1.0)
- rect &rectb
- reset
- sfx
- spr
- sync
- ttri (1.0)
- time
- trace
- tri &trib (0.90)
- tstamp (0.80)
- vbank (1.0)