0,]']>S}Wup8d%qy)6o;]AsVl3>(32-b))&__uint32limit) end function __mulberry32(a) -- mulberry32 used to seed the RNG state a=(a+0x6D2B79F5)&__uint32limit local t = ((a~(a>>15))*(1|a))&__uint32limit t = t + ((((t~(t>>7))*(61|t))~t))&__uint32limit return a, t end function __rand() -- xoshiro128** used for dice rolls local result = (__rotl(_s[1]*5,7)*9)&__uint32limit local t = (_s[1]<<9)&__uint32limit _s[2]=_s[2]~_s[0] _s[3]=_s[3]~_s[1] _s[1]=_s[1]~_s[2] _s[0]=_s[0]~_s[3] _s[2]=_s[2]~t _s[3]=__rotl(_s[3],11) return result end function math.random(a,b) if not (a or b) then return __rand()/(1<<32) elseif a and not b then return math.random(1,a) else local range=b-a if range==0 then return a end assert(range>0,"bad argument #1 to 'random' (interval is empty)") local bitmask=1 while bitmask<=range do bitmask=bitmask*2 end bitmask=bitmask-1 local rv repeat rv=__rand()&bitmask until rv<=range return a+rv end end function math.randomseed(s) s=tonumber(s)&__uint32limit local seedval for i=0,3 do s, seedval = __mulberry32(s) _s[i]=seedval end _s[0]=_s[0]|1 end function printc(s,y,c) y=y or 0 c=c or 12 local _w = print(s,0,-6) print(s,(240//2)-(_w//2),y,c) end function display_dice(n,x,y,s,f,r) s=s or 1 f=f or 0 r=r or 0 spr(254+(n*2),x,y,-1,s,f,r,2,2) end function display_hp_bar(val,max,x,y) rect(x,y,100,4,2) rect(x,y,math.floor((val/max)*100),4,5) end -- calculate level given xp value function level_for_xp(xp) -- level bottoms at 1 if xp<=0 then return 1 end -- level caps at 100 if xp>=1000000 then return 100 end return math.floor(0.099*math.sqrt(xp))+1 end -- 2 dice at level 1 -- 3 dice at level 12 -- 4 dice at level 45 -- 5 dice at level 100 -- level caps at 100 function dice_for_level(lvl) return 2+(3*math.floor(math.sqrt(lvl)/10)) end function roll_dice() return math.random(1,6) end function Creature(type,width,height) local creature = {} creature.type=type creature.health=1 creature.max_health=1 creature.level=1 creature.dice=2 creature.anim_timer=0 creature.width=width creature.height=height creature.hurt=false creature.rolls={} function creature._determine_health_for_level(lvl) return 1 end function creature.set_level(lvl) if lvl>100 then lvl=100 end if lvl<1 then lvl=1 end creature.level=lvl creature.set_health(creature._determine_health_for_level(lvl),true) creature.rolls={} -- xkcd reference for i=1,dice_for_level(lvl) do creature.rolls[i]=4 end end function creature.set_health(hp,max) creature.health=hp if max then creature.max_health=hp end end function creature._roll_dice(n) local rv = {} for i=1,n do table.insert(rv,roll_dice()) end table.sort(rv) return rv end function creature.roll_dice(n) return creature._roll_dice(n) end function creature.attack() local number_of_dice = dice_for_level(creature.level) local rolls = creature.roll_dice(number_of_dice) assert(#rolls==number_of_dice,"creature.roll_dice returned incorrect number of dice!") local sum = 0 for i=1,#rolls do sum=sum+rolls[i] end creature.rolls=rolls return sum end function creature.anim() creature.anim_timer=45 end function creature.draw(x,y,hurt) end function creature.name() return creature.type:sub(1,1):upper()..creature.type:sub(2):lower() end function creature._draw() local x=120+(120//2)-(creature.width//2) local y=(32//2)-(creature.height//2) creature.draw(x,y,creature.anim_timer>0) if creature.anim_timer>0 then creature.anim_timer=creature.anim_timer-1 end local name = creature.name()..", Level "..tostring(creature.level) print(name,120+(120//2)-50,y+creature.height+1,12) display_hp_bar(creature.health,creature.max_health,120+(120//2)-50,y+creature.height+8) end function creature.xp_yield() return creature.max_health//2 end return creature end function Blob() local creature = Creature("blob",28,22) function creature._determine_health_for_level(lvl) return math.floor(40*math.sqrt(lvl)) end function creature.roll_dice(n) -- rolls 2 more dice than required and drops highest local rv = creature._roll_dice(n+2) rv[#rv]=nil rv[#rv]=nil return rv end function creature.draw(x,y,hurt) if hurt then spr(298,x,y-4,2,2,0,0,2,2) else spr(296,x,y,2,2,0,0,2,2) end end return creature end function Snail() local creature = Creature("snail",38,26) function creature._determine_health_for_level(lvl) return math.floor(35*math.sqrt(lvl)) end function creature.roll_dice(n) -- rolls 1 more dice than required and drops highest local rv = creature._roll_dice(n+1) rv[#rv]=nil return rv end function creature.draw(x,y,hurt) if hurt then spr(294,x,y,11,2,0,0,2,2) else spr(291,x,y,11,2,0,0,3,2) end end return creature end function Ghost() local creature = Creature("ghost",36,30) function creature._determine_health_for_level(lvl) return math.floor(30*math.sqrt(lvl)) end -- default dice rolling behavior function creature.draw(x,y,hurt) -- base spr(288,x,y,11,2,0,0,3,2) if hurt then -- blush rect(x+10,y+10,2,2,2) rect(x+28,y+10,2,2,2) -- eyes rect(x+14,y+8,4,4,0) rect(x+22,y+8,4,4,0) else -- blush rect(x+12,y+12,2,2,2) rect(x+26,y+12,2,2,2) -- eyes rect(x+16,y+10,2,4,0) rect(x+22,y+10,2,4,0) end end return creature end local player = { health = 48, max_health = 48, level = 1, xp = 0, dice = 2, rolls = {4,4}, regen = 1, defeated={} } function player_health_for_level(lvl) return math.floor(48*math.sqrt(lvl)) end local creature local draw_rolls_lut={ function(x,y,rolls) display_dice(rolls[1],x+(120//2)-(16//2),y+(38//2)-(16//2)) end, function(x,y,rolls) display_dice(rolls[1],x+(120//2)-17,y+(38//2)-(16//2)) display_dice(rolls[2],x+(120//2)+1,y+(38//2)-(16//2)) end, function(x,y,rolls) display_dice(rolls[1],x+(120//2)-(16//2)-18,y+(38//2)-(16//2)) display_dice(rolls[2],x+(120//2)-(16//2),y+(38//2)-(16//2)) display_dice(rolls[3],x+(120//2)+(16//2)+2,y+(38//2)-(16//2)) end, function(x,y,rolls) display_dice(rolls[1],x+(120//2)-17,y+(38//2)-17) display_dice(rolls[2],x+(120//2)+1,y+(38//2)-17) display_dice(rolls[3],x+(120//2)-17,y+(38//2)+1) display_dice(rolls[4],x+(120//2)+1,y+(38//2)+1) end, function(x,y,rolls) display_dice(rolls[1],x+(120//2)-(16//2)-18,y+(38//2)-17) display_dice(rolls[2],x+(120//2)-(16//2),y+(38//2)-17) display_dice(rolls[3],x+(120//2)+(16//2)+2,y+(38//2)-17) display_dice(rolls[4],x+(120//2)-17,y+(38//2)+1) display_dice(rolls[5],x+(120//2)+1,y+(38//2)+1) end } function draw_rolls(x,y,rolls,obfuscate) if obfuscate then local n=#rolls rolls={} for i=1,n do rolls[i]=oldrandom(1,6) end end return draw_rolls_lut[#rolls](x,y,rolls) end local state = "mainmenu" local gt = 0 local st = 0 function changestate(s) state=s st=0 end local count=function(s,m) local _,c = s:gsub(m,"") return c end local messages = {} function message(s) if s:sub(-1)~="\n" then s=s.."\n" end for line in s:gmatch("(.-)\n") do messages[#messages+1]=line end while count(table.concat(messages,"\n"),"\n")>21 do table.remove(messages,1) end end local mm_substate=1 local mm_startanim_st=-1 local mm_substate_lut={ function() printc("PRESS A TO START",100) printc("PRESS B FOR IRON MAN MODE",116) if btnp(4) then mm_startanim_st=st mm_substate=2 math.randomseed(tstamp()~math.floor(time()*1000)) end if btnp(5) then player.regen=0 mm_startanim_st=st mm_substate=2 end end, function() cls(14) local at = st-mm_startanim_st rect(-120+(120/45*at),0,120,136,15) print(table.concat(messages,"\n"),-120+3+(120/45*at),3,12) rect(240-(120/45*at),136-(66/45*at),120,66,13) draw_rolls(240-(120/45*at),136-(66/45*at),player.rolls) print("Player, Level "..player.level,250-(120/45*at),181-(66/45*at)) display_hp_bar(player.health,player.max_health,250-(120/45*at),123+(66/45*at)) if at==45 then changestate("gameplay") end end } function _mainmenu() cls(14) spr(448,120-64,16,11,1,0,0,16,4) spr(384,120-36,42,14,1,0,0,9,4) mm_substate_lut[mm_substate]() end local cooldown=0 local toggle=false function _gameplay() cls(14) rect(0,0,120,136,15) rect(120,70,120,66,13) print(table.concat(messages,"\n"),3,3,12) if not creature then local n=math.random(1,3) if n==1 then creature=Blob() elseif n==2 then creature=Snail() else --n==3 creature=Ghost() end local target_xp=player.xp+math.random(-100,100) creature.set_level(level_for_xp(target_xp)) message("A level "..creature.level.." "..creature.name().."\nappears!") end creature._draw() draw_rolls(120,32,creature.rolls,cooldown==0) draw_rolls(120,70,player.rolls,cooldown==0) print("Player, Level "..player.level,130,115) display_hp_bar(player.health,player.max_health,130,123) if cooldown==0 then if creature.health<=0 then message("You defeat the\n"..creature.name().."!") if not player.defeated[creature.type] then player.defeated[creature.type]=1 else player.defeated[creature.type]=player.defeated[creature.type]+1 end if player.regen then local regen = math.ceil(player.regen*((player.max_health-player.health)/2)) regen=math.min(regen,player.max_health-player.health) if regen>0 then message("You take a breather\nand regenerate\n"..regen.." health!") end player.health=player.health+regen end player.xp = player.xp + creature.xp_yield() creature=nil local new_level = level_for_xp(player.xp) if new_level>player.level then player.level=new_level changestate("levelup") return end return _gameplay() -- to get a new one end if player.health<=0 then changestate("gameover") end if (btnp(4) or btnp(5) or toggle) then player.rolls={} local sum=0 for i=1,player.dice+1 do player.rolls[i]=roll_dice() end table.sort(player.rolls) table.remove(player.rolls,1) -- drop lowest for i=1,#player.rolls do sum=sum+player.rolls[i] end local enemy_sum=creature.attack() message("You roll: "..sum) message(creature.name().." rolls: "..enemy_sum) if sum>enemy_sum then local damage=sum-enemy_sum message("You deal "..damage.." damage\nto the "..creature.name().."!") creature.health=creature.health-damage creature.anim() elseif enemy_sum>sum then local damage=enemy_sum-sum message(creature.name().." deals "..damage.." damage\nto you!") player.health=player.health-damage else message("Neither side gets an\nadvantage...") end cooldown=90 end else cooldown=cooldown-1 end if btnp(6) then toggle=not toggle end end function _levelup() if st==0 then player.level = level_for_xp(player.xp) local dice = dice_for_level(player.level) player.gained_dice_last_levelup = dice>player.dice player.dice=dice end if st==45 then local delta_health = player_health_for_level(player.level)-player.max_health player.health=player.health+delta_health player.max_health=player.max_health+delta_health music(0,-1,-1,false) end cls(14) if st<45 then rect(0-(120/45*st),0,120,136,15) print(table.concat(messages,"\n"),3-(120/45*st),3,12) rect(120+(120/45*st),70+(66/45*st),120,66,13) draw_rolls(120+(120/45*st),70+(66/45*st),player.rolls) print("Player, Level "..player.level,130+(120/45*st),115+(66/45*st)) display_hp_bar(player.health,player.max_health,130+(120/45*st),123+(66/45*st)) elseif st>255 then local at=st-255 rect(-120+(120/45*at),0,120,136,15) print(table.concat(messages,"\n"),-120+3+(120/45*at),3,12) rect(240-(120/45*at),136-(66/45*at),120,66,13) draw_rolls(240-(120/45*at),136-(66/45*at),player.rolls) print("Player, Level "..player.level,250-(120/45*at),181-(66/45*at)) display_hp_bar(player.health,player.max_health,250-(120/45*at),123+(66/45*at)) if st==300 then changestate("gameplay") end else if ((st//20)&1)==1 then return end local y=136//2 if player.gained_dice_last_levelup then y=y-((12*4+6)//2) else y=y-((12*2+6)//2) end printc("LEVEL UP!",y) y=y+12 printc("YOU ARE NOW",y) y=y+12 printc("LEVEL "..player.level,y) y=y+12 if player.gained_dice_last_levelup then printc("YOU GAIN 1 DIE!",y) y=y+12 printc("YOU NOW HAVE "..y.." DICE!",y) end end end function _gameover() cls(14) if st<45 then rect(0-(120/45*st),0,120,136,15) print(table.concat(messages,"\n"),3-(120/45*st),3,12) rect(120+(120/45*st),70+(66/45*st),120,66,13) draw_rolls(120+(120/45*st),70+(66/45*st),player.rolls) print("Player, Level "..player.level,130+(120/45*st),115+(66/45*st)) display_hp_bar(player.health,player.max_health,130+(120/45*st),123+(66/45*st)) else spr(448,120-64,8,11,1,0,0,16,4) spr(384,120-36,36,14,1,0,0,9,4) printc("GAME OVER",70) printc("FINAL LEVEL: "..player.level,78) local y=86 for k,v in pairs(player.defeated) do printc(k:sub(1,1):upper()..k:sub(2):lower().."s defeated: "..v,y) y=y+8 end y=y+8 printc("PRESS A OR B TO RETURN TO MAIN MENU",y) if btnp(4) or btnp(5) then player = { health = 48, max_health = 48, level = 1, xp = 0, dice = 2, rolls = {4,4}, regen = 1, defeated={} } messages={} creature=nil toggle=false mm_substate=1 changestate("mainmenu") end end end function TIC() _G["_"..state]() gt=gt+1 st=st+1 end