A short dungeon crawler.
Made for Dungeon Crawler Jam 2021.
Credits:
- Programming, art : sprvrn
- Music : Nyeti
- Raycasting from lode vandevenne's tutorial (licence included in source code)
Amazing! Love the 3d view and I like the approach to combat.
This was so fun! I think blue is the most OP, those needles work well on anything!
Neat little game! The graphics work well, and I like the whole maze design.
Have a bug to report, though - a random encounter was supposed to start and something crashed. This is the error message as copied from the command line:
attempt to get length of local 'c' (a number value) at line 0 (tab 0) |
...and here's a screenshot with more detail:
@sprvrn I looked at your raycast code and spotted why walls appears to be "turning" up close. Good idea to clip start/end segment but unless you clip also texture coordinates, it "squeezes" segment, producing this bug.
I also added the standard "sub-pixel" trick to avoid jaggies on the walls.
function draw_cast(scx,scy) rectfill(scx,scy,viewsize+scx,viewsize/2+scy,ceil_clr) rectfill(scx,viewsize/2+scy,viewsize+scx,viewsize+scy,flr_clr) -- *************************** -- to avoid overwriting borders clip(3,3,122,122) local texsize,sprsize,px,py=8,8, posx+ofx+.5-(dirx*.7), posy+ofy+.5-(diry*.7) for x=0,viewsize do local camerax=2*x/viewsize-1 local raydirx,raydiry,mapx,mapy,sidedistx,sidedisty,hit,stepx,stepy,perpwalldist,side,sprt,wallx,sprx,drawstart,drawend=dirx+planex*camerax,diry+planey*camerax,flr(px),flr(py),0,0,0 local deltadistx,deltadisty=abs(1/raydirx),abs(1/raydiry) if raydirx<0 then stepx,sidedistx=-1,(px-mapx)*deltadistx else stepx,sidedistx=1,(mapx+1-px)*deltadistx end if raydiry<0 then stepy,sidedisty=-1,(py-mapy)*deltadisty else stepy,sidedisty=1,(mapy+1-py)*deltadisty end while hit==0 do if sidedistx<sidedisty then sidedistx+=deltadistx mapx+=stepx side=0 else sidedisty+=deltadisty mapy+=stepy side=1 end sprt=mget(mapx,mapy) if(sprt>0 and fget(sprt,0))hit=1 end if side==0 then perpwalldist=(mapx-px+(1-stepx)/2)/raydirx else perpwalldist=(mapy-py+(1-stepy)/2)/raydiry end local lh=viewsize/perpwalldist drawstart=-lh/2+viewsize/2 -- ******************************** -- not needed (or fix texture start) -- if(drawstart<0)drawstart=0 drawend=lh/2+viewsize/2 -- ******************************** -- not needed (or fix texture end) -- if(drawend>=viewsize)drawend=viewsize-1 if side==0 then wallx=py+perpwalldist*raydiry else wallx=px+perpwalldist*raydirx end wallx-=flr(wallx) sprx=flr(wallx*texsize) if(side==0 and raydirx>0)sprx=texsize-sprx-1 if(side==1 and raydiry<0)sprx=texsize-sprx-1 local sx,sy=sprwallcrd(sprt) --if(side==1)shadow() -- ******************************** -- shift texture height to accomodate sspr flooring sspr(sx+sprx,sy,1,texsize,x+scx,drawstart+scy,1,ceil(drawend)-ceil(drawstart)) --pal(palette) zbuffer[x]=perpwalldist end for i=1,#objects do local o=objects[i] o.distance=(px-o.x)*(px-o.x)+(py-o.y)*(py-o.y) end sort(objects,function(a,b)return a.distance<b.distance end) for i=1,#objects do local o=objects[i] if not o.hide then local spritex,spritey=o.x+.5-px,o.y+.5-py local invdev=1.0/(planex*diry-dirx*planey) local transformx=invdev*(diry*spritex-dirx*spritey) local transformy=invdev*(-planey*spritex+planex*spritey) local spritescreenx=flr((viewsize/2)*(1+transformx/transformy)) local vmovescreen=flr(o.vmove/transformy) local spriteheight=abs(flr(viewsize/(transformy)))/o.vdiv local drawstarty=-spriteheight/2+viewsize/2+vmovescreen -- ******************************** --if(drawstarty<0)drawstarty=0 local drawendy=spriteheight/2+viewsize/2+vmovescreen -- ******************************** --if(drawendy>=viewsize)drawendy=viewsize-1 local spritewidth=abs(flr(viewsize/transformy))/o.udiv local drawstartx=-spritewidth/2+spritescreenx if(drawstartx<0)drawstartx=0 local drawendx=spritewidth/2+spritescreenx if(drawendx>=viewsize)drawendx=viewsize-1 for stripe=drawstartx,drawendx do local texx=flr((stripe-(-spritewidth/2+spritescreenx))*sprsize/spritewidth) -- useless stripe>0 and stripe<viewsize test if transformy>0 and transformy<zbuffer[flr(stripe)] then local sx,sy=shtcrd(o.sprite) sspr(clamp(sx,sx+texx,sx+sprsize-1),sy,1,sprsize,stripe+scx,drawstarty+scy,1,ceil(drawendy)-ceil(drawstarty)) end end end end clip() end |
@freds72
Thank you so much for digging into my code and fixing it! It looks way better now :p
That was great, I enjoyed that! A few suggestions for improvement:
-When Blue reaches level 4, two of the numbers in her display go past the right edge of the game, the max HP and the Bio+ cost.
-"Nuclear" is spelled wrong, which I'd write off as intentional if it weren't misspelled in two different ways, "nuklear" and "nucklear". I also can't tell if Bio+ being described as a nuclear attack just means it hits all the enemies, or if that's an error.
-Less important, but there are some huge balance issues. Dew+ shouldn't cost 5 SP, that is way too cheap when normal Dew also costs 5. Bio+, on the other hand, seemed a bit too expensive. Maybe it should cost 10? I was certainly never going to use it without a lot more SP on Blue. And I think the SP discrepancy between Red and Green is a bit too big... I appreciated having plenty to heal with, but it kind of removed a lot of the challenge, and when Red got Dew it seemed a little pointless without more SP.
That last boss was really punishing, but not for a very good reason imo... Only Cyber seemed super effective against it, anything else barely made a dent, but only Green had Cyber so she had to choose between healing and attacking while everyone constantly took huge hits. Red can use Dew, but only three times, so I ended up losing her and barely scraped by with the other two. Maybe I'm just not used to how difficulty works in turn-based RPGs, I haven't played many...
(Also the save points there are too close together, it's easy to get from one to the other without an encounter so there's no point in the first one imo.)
[Please log in to post a comment]