﻿-- katamari christmassy
-- by @p01 and @gasmanic

function pp(t,x,y,c)
 ?t,x,y,1
 ?t,x,y-1,c
end

function draw_katamari()
  -- todo? adjust the size of the source sprite to scale up only if needed
  s = max(8,ball.size * 2);
  ox = 128-flr(ball.x/64)*256
  oy = 128-flr(ball.y/64)*256
  oz = _.state == "in_game" and 2 or 0
  for j=0,3 do
   a=ball.dir+j/4
   dx=ball.x-s/2+(j-1.5)*oz*cos(p.dir)+sin(a)
   dy = ball.y-s-(j-1.5)*oz*sin(p.dir)+cos(a)
   sspr(j*32,64,32,32, dx, dy,s,s);
   sspr(j*32,64,32,32, dx, dy+oy,s,s);
   sspr(j*32,64,32,32, dx+ox, dy,s,s);
   sspr(j*32,64,32,32, dx+ox, dy+oy,s,s);
  end
end

function _init_level(l)
 seed += 1
 srand(seed)
 -- clear the 4 sheets of katamari
 memset(4096, 0, 2048);
 _.level = l
 _.antlers = l % 6 > 3
 _.perfect_item = 0
 _.constellation_stage = _.mode == "arcade" and _.level % 2 < 1
 _.base = _.constellation_stage and 8-band(_.level,2)*2 or flr(rnd(13))
 _.obj_max_count = 0
 _.obj_picked = {}
 _.snow = _.level %2<1
 _.maxtick = (_.constellation_stage and l+1*0 or l) * 3600;
 _.goal = _.constellation_stage and flr(5 + 8/3600*_.maxtick) or flr(5+5*l^1.16);
 p={type="player";x=64;y=64;z=0;vz=0;s=8; dir=.25;dirt=.25;tick=0}
 ball={
  type="ball";
  x=64;y=64;z=0;vz=0;s=8;
  size=5;
 }
 objs={p,ball}
 for i=0,17 do
  _.obj_picked[i] = 0
  add(objs, new_obj())
 end

 -- prepare maps
 local m={}
 s = 1+rnd(2)
 for i=0,255 do
  m[i]=flr(rnd()^(_.snow and 2 or 1)*2)
 end
 for y=0,15 do for x=0,15 do
  i=m[flr(x/s)%8+flr(y/s)%8*9]+m[flr((x+1)/s)%8+flr(y/s)%8*9]*2+m[flr((x+1)/s)%8+flr((y+1)/s)%8*9]*4+m[flr(x/s)%8+flr((y+1)/s)%8*9]*8;
  mset(x+16,y,i+flr((i+15)/16)*47)
  j=max(0,rnd(18)-14+flr(i/8))
  mset(x,y, (i==0 or i==15) and j+flr((j+3)/4)*31 or 0)
 end end
end

seed=stat(93)*3600+stat(94)*60+stat(95)
obj_names = {"carrot","banana", "mushroom", "shoe", "bear", "bottle", "floppie", "car", "cow", "present", "sugar cane", "gift", "teapot"}
function new_obj()
 seed+=1
 srand(seed)
 base = _.constellation_stage and _.base or (_.level-1)*3;
 range = _.mode == "story" and 4 or _.constellation_stage and 1 or 13
 s = rnd(ball.size)+ball.size/2;
 if (_.constellation_stage) then
  s %= _.goal * .8
  if (ball.size >= _.goal and _.perfect_item<1) s=_.goal _.perfect_item=1
 end
 return {
  type= "gift";
  si = s==_.goal and 0 or 1;
  spr = flr(base+rnd(range))%13+1;
  x = rnd(112) + 8;
  y = rnd(112) + 8;  
  s = max(5, flr(s));
  z = rnd(128);
  vz = 0;
  t = 0;
 }
end

sm = {}
color_flicker = false;
function _init()
 cpu=0
 _={
  obj_max_index = 1;
  obj_max_count = 0;
  tick_halfclock = 0; 
  tick_lowpass = 0;
  tick_reverb = 0;
  tick_crush = 0;
 }
 switch_color_flicker()
 switch_mode()
end

function switch_color_flicker()
 color_flicker = not color_flicker
 menuitem(2, (color_flicker and "dIS" or "eN").."ABLE FLICKER", switch_color_flicker)
end

function switch_mode()
 j = _.mode != "story"
 _.mode = j and "story" or "arcade"
 menuitem(1, j and "sTORY ⌂ aRCADE" or "aRCADE ⌂ sTORY", switch_mode)

 _.level = 0
 _.s = {}
 new_state("game_intro")
end

function _update60()
 cls()
 cols={1, 7, 7,6,13,5,1}
 state_before = _.state
 sm[_.state]()
 
 memset(24384,0,4)
 if (_.tick_halfclock>0) poke(24384, 15) _.tick_halfclock-=1
 if (_.tick_reverb>0) poke(24385, 15) _.tick_reverb -=1
 if (_.tick_crush>0) then
  poke(24386, 15) _.tick_crush -=1
  srand(seed) seed+=1
  for i=0,40 do
   local j=flr(rnd(126))*64+24641
   memcpy(j,j+rnd(3)-1,62)
  end
 end
 if (_.tick_lowpass>0) poke(24387, 15) _.tick_lowpass -=1

 if( state_before == _.state) tick+=1


 cpu = stat(1)
end

function new_state(s)
 state_before = _.state
 _.state = s
 tick = 0
 if(state_before != _.state) then
  if(s=="game_intro" or s=="level_over") music(0)
  if(s=="in_game") music(8)
 end

end


sm.game_intro = function ()
 king_and_earth()

 tt="kATAMARI ⌂ cHRISTMASSY"
   pp(tt,64-#tt*2,36,8+(color_flicker and tick%8 or tick/30%8))
tt=({"BY @p01 AND @gasmanic", "A PICO8+XMAS HOMAGE TO THE","kATAMARI SERIES BY namco", _.mode.." mode ⌂", "pause TO SWITCH mode", "pRESS ❎ TO START"})[flr(max(0,t()/3-1)%6)+1]
 pp(tt, 64-#tt*2, 44, 13)

 if (btnp(❎)) new_state("level_intro")
end

sm.level_intro = function()
 if(tick==0) then
 _init_level(_.level + 1)
  tx,ty,tyo=64,32,50
  txs={_.antlers and "lOOK WE HAVE ANTLERS" or _.snow and "oH! oH! oH!" or "wE WERE VERY NAUGHTY",
  _.antlers and "WE ARE SO FESTIVE!" or _.snow and "wE ARE sANTA!" or "THE STARS ARE ALL BROKEN",
  "",
  "",
  "TAKE A KATAMARI,",
  "AND FIX THE STARS",
  "",
  "",
  "WALK FORWARD WITH ⬆️",
  "STEER WITH ⬅️ AND ➡️",
  "",
  "",
  "COLLECT THINGS TO",
  "BUILD UP YOUR KATAMARI",
  "",
  "",
  "WE WILL MAKE A STAR WITH IT",
  "",
  "",
  "",
  "THE GOAL FOR THIS KATAMARI",
  "",
  (_.constellation_stage and "cOLLECT MOST "..obj_names[_.base+1].."s" or _.goal .."CM") .. " IN "..flr(_.maxtick/60/60).."MIN",
  "",
  "",
  "",
  "WE THINK YOU CAN DO IT",
  "LITTLE PRINCE",
  "",
  "",
  "",
  "SENDING THINGS TO EARTH!"
  }
 end

 king_and_earth(true)
 tye = scroll_text(txs, cols)

 rainbow(1-tye/10)
 if(tye<0) new_state("in_game")
end

function scroll_text(txs, cols)
 tyo -= 1/6
 if(btn(🅾️)) tyo-=1 _.tick_crush = 1
 for i=1,#txs do
  col=cols[flr(tyo/6)+1]
  tt=txs[i]
  if(col!=nil) pp(tt,tx-#tt*2,ty+tyo,col)
  tyo+=6
 end

 r = tyo
 tyo-=#txs*6
 return r
end

function rainbow(j)
 if(j>0) _.tick_reverb = 1 _.tick_lowpass = 1
 for k=1,j*96 do
  line(kx-1+k*2/96,ky+19,16+k,128,8+k*8/96)
 end
end

function nebula(tilt)
 srand(37)
 fillp(color_flicker and tick%2>0 and 0xf0f0.8 or 0x0f0f.8)
 for i=0,90,.2 do
  a = i+i/260+rnd(.1)-t()/50
  j=i+rnd()*tilt*4
  circfill(64+j*cos(a),64+j*tilt*sin(a),(1-i/90)*12*tilt-rnd(),1)
 end
 fillp()
 
 local pu,pv
 for j=0,#_.s do
  i=rnd(35)+15
  a = i+i/260+rnd(.1)-t()/50
  u=60+i*cos(a)
  v=60+i*tilt*sin(a)
  if(_.state=="star_map" and j>1) line(pu+4,pv+4,u+4,v+4,1)
  if(j>0)spr(63, u,v)
  pu,pv=u,v
 end

 stars(tilt)
end

function stars(tilt)
 srand(37)
 for i=0,10 do
  u=rnd(128)
  v=64+(u%1*128-64)*3*tilt
  circ(u,v,1,1)
  pset(u,v,13)
 end
end

function earth(x,y)
 --earth
 fillp(color_flicker and tick%2>0 and 0xa5a5 or 0x5a5a)
 circfill(x,y,33+8,1)
 circfill(x,y,33+6,1+16*1)
 circfill(x,y,33+4,12+16*1)
 fillp()
 circfill(x,y,33+2,12)
 circfill(x,y,33,7)
 if(_.state=="game_intro") s=flr(tick/600%1.3) sspr(8*9,0,8,8,x-12+24*s,y-56,24-48*s,24)
 if(_.state=="game_over") s=flr(tick/60+2)%8 sspr(8*s,8,8,8,x-8,y-48,16,16)
 sspr(80,8,32,16 , x-32,y-32,64,32)
end

function king_and_earth(talking, tkx, tky)
 --bg + nebula
 nebula(1/3)

 --king-roll
 kx= tkx!=nil and tkx or 64+2*cos(t()/2)
 ky= tky!=nil and tky or 4+2*sin(t()/3)
 king(talking, 32,16+cos(t()/5))

 ex=64
 ey=max(128,128+48-t()*12)
 earth(ex,ey)
end

function king(talking, w, w2)
-- for i=-24,24 do line(kx+i,ky+24,kx,127,0) end
 rectfill(kx-w,ky+12,kx+w,ky+20,2)
 rectfill(kx-(w-1),ky+13,kx+(w-1),ky+19,4)
 rectfill(kx-w,ky+14,kx+w,ky+17,8)
 clip(kx-w,ky+12,w*2,9)
 srand(46)
 for i=0,7 do
  ?"★\n\n★",kx-w+i*w/4,ky+12-12+(rnd(12)-t()*10)%12,9
 end
 clip(kx-w,ky+13,w*2,6)
 srand(46)
 for i=0,7 do
  ?"★\n\n★",kx-w+i*w/4,ky+12-12+(rnd(12)-t()*10)%12,10
 end
 clip()
 if(w2 != nil) then
 -- ruffle
  ky+=sin(t()*.7)
  rectfill(kx-w2, ky+20, kx+w2,ky+25,1)
  clip(kx-w2, ky+20,w2*2+1,6)
  for w=-w2,w2 do
   y=2*sin(2*cos(w/w2/4))
   line(kx+w,ky+23-y-3, kx+w, ky+23-y+3,13)
   line(kx+w,ky+23-y-1, kx+w, ky+23-y+1,7)
  end
  clip()
  ky-=sin(t()*.7)
 end

 if(_.antlers) spr(66,kx-16,ky+6,2,1)spr(66,kx,ky+6,2,1,true)
 kh = (_.snow and not _.antlers) and 14 or 15
 spr(kh,kx-8,ky,1,1)spr(kh,kx,ky,1,1,true)
 local s = 15+16
 if(tick/9%13<1) s-=1
 spr(s,kx-8,ky+8) spr(s,kx,ky+8,1,1,true)
 s=15+32
 if(talking and tick/16%2>1) s-=1
 spr(s,kx-8,ky+16)spr(s,kx,ky+16,1,1,true)
 srand(seed)
end

sm.star_map = function()
 tilt = 1/3 - 1/3 * sin(tick*.5/60/7)
 srand(seed) seed += 1
 nebula(tilt)

 tx = (_.constellation_stage and ("gREAT "..obj_names[_.base+1]) or (_.mode == "story" and "cHRISTMAS" or "mISTERY")).." CONSTELLATION";
 pp(tx,64-#tx*2-8+tilt*12,96,13)

 s = 96 * (1 - tick / 60)
 fillp()
 circfill(64,64,s+2,12)
 circfill(64,64,s,7)
 if(s>0) _.tick_lowpass = 4

 if (tilt < 1/3) new_state((_.mode == "story" and _.level==4) and "game_over" or "level_intro")
end

sm.level_over = function()
 kx = 40+cos(t()/2)
 ky = 56+2*sin(t()/3)
 king_and_earth(false, kx, ky)

 circfill(kx-2,ky+24,4,0)
 rhx=128-40
 rhy=128-24+2*sin(t()/2)
 
 lhx = 84+sin(t()/2)
 lhy = 84
 if(tick==0) then
  srand(t())
  ball.y = lhy
  tx,ty,tyo=80,0,50
  win = ball.size >= _.goal;
  s=flr(ball.size*100)/100
  if(win) add(_.s, s);
  win_constellation = win and _.constellation_stage and _.perfect_item > 1;
  txs={
    win_constellation and "tHIS IS incredible!" or (win and (rnd()>.5 and "wELL," or "nOT bad")) or "wHAT IS THIS?",
    "",
    "",
    "THIS IS "..s.."CM",
    "",
    "",
    "",
    "wE ARE MOVED TO TEARS BY",
    "THE SIZE OF THIS THING",
    "",
    "",
    "",
    win_constellation and ("tHIS IS THE perfect "..obj_names[_.obj_max_index]) or "IF IT WAS OUR KATAMARI",
    win_constellation and ("we ♥ "..obj_names[_.obj_max_index].."s") or "wE'D MAKE IT MUCH BIGGER",
    "",
    "",
    win_constellation and "" or (win and "bUT THAT WILL DO" or "wE CAN'T MAKE A STAR "),
--    "",
    "",
    "",
    win and "lET's RELEASE THIS STAR" or "gO BACK TO EARTH"
  }
 end

 sspr(8,32, 8,8, rhx-12,rhy-6,16,16)
 fillp(0xa5a5)
 for i=1,10 do
  line(kx,ky+26,rhx+2,rhy+i,flr(i/8))
  line(kx,ky+26,lhx-10,lhy-4+i/2,flr(i/10))
 end
 sspr(0,32, 8,8, rhx-12,rhy-6,16,16)
 fillp()
 x=kx-8-cos(t()/3)
 circfill(x+6,ky+24,4,0)
 spr(0,x,ky+28)
 king(tick < 20 * 60, 24,16)

 cols=win and {1, 7, 7, 6,13,5,1} or
              {1,14, 14, 8, 2,2,1}
 tye = scroll_text(txs, cols)

 -- king's hands
 sspr(64,8,16,16,rhx,rhy,32,32)
 spr(24,lhx-10,lhy-4,2,2)

 -- ball & katamari
 ball.x = lhx
 spr(38,ball.x-4,ball.y-16)
 draw_katamari()

 -- the prince
  spr(22,rhx+22,rhy)

 if win_constellation then
  -- and the perfect item
  sspr(_.obj_max_index*8,0,8,8, rhx+24,rhy-20,-24,24)
 else
  -- and his favorite item
  spr(_.obj_max_index, 0, 110)
  pp("X".._.obj_max_count,10,114,13)
 end

 if (win) then
  next_state = (_.mode == "story" or _.constellation_stage) and "star_map" or "level_intro"
 else 
  next_state = _.constellation_stage and "level_intro" or "in_game"
 end

 j=max(0,1-tye/10)
 if(win) then
  ball.y = lhy - j * 32
  s= j*192-64
  fillp()
  if(s>0) _.tick_lowpass = 4
  circfill(ball.x,ball.y-8-ball.size/2,s+2,12)
  circfill(ball.x,ball.y-8-ball.size/2,s,7)
 end
 if(next_state == "in_game") rainbow(j)
 if(tye<0)_init_level(_.level) new_state(next_state)
end

sm.game_over = function()
 if(tick==0) then
  tx,ty,tyo=64,44-10,50
  txs={
    "cONGRATULATION!",
    "",
    "",
    "=★ dASHING pRINCE",
    "",
    "",
    "tHe gREAT kING OF aLL cOSMOS",
    "bEAUTIFUL qUEEN OF aLL cOSMOS",
    "",
    "",
    "THE BEARS, THE COWS,",
    "AND ALL CREATURES",
    "",
    "",
    "aLL LIVE IN A BEAUTIFUL WORLD",
    "",
    "",
    "",
    "⌂",
    "",
    "",
    "",
    "★ CREDITS ★",
    "",
    "",
    "CODE:      ",
    "mATHIEU @p01 hENRI",
    "",
    "",
    "VISUALS:      ",
    "mATHIEU @p01 hENRI",
    "",
    "",
    "MUSICS:      ",
    "mATT @gasmanic wESTCOTT",
    "",
    "",
    "oRIGINAL katamari SERIES",
    "namco",
    "",
    "",
    "",
    "♥",
    "",
    "",
    "",
    "PEACE ON THE WHOLE COSMOS",
    "",
    "",
    "",
    "⌂",
    "",
    "",
    ""
  }
 end

  king_and_earth(true)
  tt="kATAMARI ⌂ cHRISTMASSY"
  cols[3]=8+(color_flicker and tick%8 or tick/30%8)
  cols[4]=cols[3]

 tye = scroll_text(txs, cols)

 if(tye<0) switch_mode()
 if (btnp(❎)) switch_mode()
end

sm.in_game = function()
 --bg
 local bg,bg2 = _.snow and 6 or 3, _.snow and 13 or 4
 cls(bg)
 pal(4,bg2)
 if(_.snow and color_flicker and tick%2>0) pal(11,3) pal(5,6)
 map(16)
 pal(4,4)
 map()
 pal(5,5)
 local bumped = false
 --player
 speed=.5/stat(7)
 if(btn(⬅️)) p.dirt-=speed
 if(btn(➡️)) p.dirt+=speed
 if(btn(⬆️)) p.x+=64*speed*cos(p.dir) p.y-=64*speed*sin(p.dir) p.tick+=1
 if(btnp(⬇️)) p.dirt+=.5 _.tick_reverb = 16
 p.dir=p.dir*.8+p.dirt*.2
 p.spr=flr(p.dir%1*4+.5)%4*2+16+flr(p.tick/8%2)

 --ball
 ball.dir = (ball.y-ball.x)/16;
 ball.spr=38-flr(ball.dir*4)%3;
 ball.x = p.x+8*cos(p.dir)
 ball.y = p.y-8*sin(p.dir)

 -- sort objs y sorted
 for i=1,#objs do
  -- wrap
  local obj = objs[i]
  obj.x%=128
  obj.y%=128
  -- sort
  for j=1,i-1 do
   if objs[j].y>objs[i].y then
    local oi = objs[i];
    objs[i] = objs[j];
    objs[j] = oi;
   end
  end
 end

 -- draw objs y sorted
 for i=1,#objs do
  obj = objs[i]
  if obj.type != ":(" then
   local s=obj.type == "gift" and min(48,obj.s) or obj.s;
   local sx,sy=obj.spr%16*8,flr(obj.spr/16)*8
   local x,y=obj.x-s/2,obj.y-s;
   obj.tooBig = obj.type == "gift" and obj.s > ball.size;
   --sspr(0,0,8,8,x,y+s/8,s,s)
   rectfill(x+s*.3,y+s*.75,x+s*.87,y+s*.87,2)
   rectfill(x+s*.1,y+s*.87,x+s*.7,y+s)
   sspr(sx,sy,8,8,x,y-obj.z,s,s)
   if(obj.z>0) then
    obj.vz +=.1;
    obj.z -= obj.vz;
    if(obj.z < 0) then
     if (obj.t > 30 or obj.vz < .2) obj.z = 0
     obj.vz *= -.3 obj.z *= -1;
    end
   end


   if(obj.type=="ball") draw_katamari()

   if(obj.type=="gift" and obj.z<2) then
    obj.t += 1
    if(obj.si * obj.t>120 and obj.s>ball.size) obj.t=0 obj.s-=1
    local d = (obj.x-ball.x)^2 + (obj.y-ball.y)^2
    if(d<64) then
     if (obj.tooBig) then
      bumped = true
      p.x-=4 * 64*speed*cos(p.dir)
      p.y+=4 * 64*speed*sin(p.dir)
      _.tick_crush = 4
      if(obj.s*.9>ball.size) ball.size -=.03  _.tick_crush = 16
     else
      _.perfect_item += 1 - obj.si
      c = _.obj_picked[obj.spr] + 1
      _.obj_picked[obj.spr] = c
      if(c >= _.obj_max_count) _.obj_max_count = c _.obj_max_index = obj.spr 
      ball.size += .1
      _.tick_reverb = 16
      objs[i] = new_obj()
      if (cpu > .8) objs[i].type = ":("
     -- splat the gift into the big katamari sprites
      dx=flr(rnd()^3*4)
      s = rnd(12-dx)
      dx=dx*32+12+s*cos(s);
      dy=64+12+s*sin(s);
      for x=0,7 do
       for y=0,7 do
        c=sget(sx+x,sy+y)
        if(c>0) sset(dx+x,dy+y,c) sset(dx+x+(tick+y)%2,dy+y+1,2)
       end
      end
     end
    end
   end
   end

 end

 if(_.snow) then
  srand(47)
  for i=0,128 do
   local y=(rnd(128)+t()*8)%(i*7%128)
   pset(i+rnd(4)+2*cos(y/6),y,7)
  end
  srand(t())
 end

 -- hud
 fp=color_flicker and tick%2>0 and 0xa5a5.8 or 0x5a5a.8
 fillp(fp)
 circfill(8,4,24,1)
 --ball size
 local bs=min(22,2+18*ball.size/(_.goal > 0 and _.goal or 20))
 circfill(8,4,min(22,bs+2),10)
 circfill(8,4,bs,11)

 tx=128-13+4
 ty=13+4

 -- king of all cosmos?
 kt=7
 ki=tick/300%kt>kt-1 and flr(1+tick/60/kt+_.base) or 0
 kx=112
 ky= 4
 kt=({
 "wE WERE NAUGHTY",
 "wHY SO TINY?",
 "we ♥ "..obj_names[_.obj_max_index].."s",
 "Oh! Oh! Oh!",
 "aRE YOU GREEN OR YELLOW?"
 })[flr(ki%4+1)]
 if(ki>0) circfill(kx,ky+16,24,1) pp(kt,kx-#kt*4,ky+27,7) kx+=cos(t()/5) ky+=sin(t()/3) king(true, 4)

 fillp(0x9f9f.8)
 circ(128,128+8+16,40+t()*16%16,7)
 fillp()
 circfill(128,128+16,33,7)
 sspr(80,8,16,8,128-32,128-16,32,16)


 --spr(p.spr,120-4,120-4)
 x=128-20
 y = 128-6-20
-- rectfill(x+2,y+14,x+12,y+18,6)
 sspr(p.spr%8*8,8,8,8,x,y,16,16)


 pp(flr(ball.size*100)/100,12+bs,2,7)
 if(_.goal > 0) pp(_.goal,5,26,7)
 if(bumped and color_flicker and tick%2>0) cls(7)
 pal()

 -- flash to black in the last X seconds
 if(rnd() < min(1,(tick-_.maxtick)/60/3+1)) then
  if (color_flicker) cls()
  _.tick_lowpass = 32
 end

 local left = flr((_.maxtick-tick)/60)
 local s="0"..(left%60)
 pp("⧗"..flr(left/60)..(tick%60<30 and ":" or " ")..sub(s,#s-1).."  ".._.mode.." #".._.level, 1,121,({8,8,9,10,15,7})[mid(1,6,flr(.9+left/5+rnd()))])

 if(tick>_.maxtick) new_state("level_over")
end
