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.
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
|
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]



