Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
648 lines (571 sloc) 17.5 KB
#Copyright (c) 2016 Fedor Kalugin
#MIT License
BEGIN {
#USER SETTINGS
#screen width and height, every "pixel" is 2 chars wide
w=64
h=48
#default color mode, change at runtime by pressing 1-4
#1 = no color, chars only, fast drawing
#2 = colored chars
#3 = background color only
#4 = background color with char textures
colormode = 4
#INITIALIZATION
srand()
PROCINFO["sorted_in"] = "@ind_num_asc"
buffer[w,h]
ZBuffer[w]
#ugly 2d array initialization
sprite[0][0]
delete sprite[0]
reloadTimeLeft = 0;
moveSpeed = 0.8
rotSpeed = 0.4
reloadTime = 10
score = 0
health = 100
moves = 1001
#key bindings
EXIT_KEY = "q"
MOVF_KEY = "w"
MOVB_KEY = "s"
MOVL_KEY = "a"
MOVR_KEY = "d"
ROTL_KEY = "j"
ROTR_KEY = "l"
FIRE_KEY = " "
UWIN_KEY = "x"
#initial player direction vector
dirX = 0.0
dirY = -1.0
#camera plane perpendicular to direction vector
planeX = -0.66
planeY = 0.0
#LEVEL DESIGN
mapWidth=44
mapHeight=44
map =\
"55555555566666666665555566666666655556666666"\
"5........666.6.6.65.....6...66665....7666666"\
"5.8.8.8.............88..6...66667.8..7.....6"\
"5........66666.6.6888...6.7.6667..88.7.....6"\
"5.8.8.8......6.6.6668..66.7.667......7.....6"\
"5........555.666..668.....7.65...7555666.666"\
"5.8.8.8..5.5.6667.766555557.67..75...666.666"\
"5....5...5...667...766665...6..76677.666.666"\
"555.5566.555.67.....76665.666..56667.66..666"\
"668.8666.....67..8..76665.....766667.6...666"\
"67...77666666667...766665....7666667....6666"\
"67....7666666666555666666777..566667...66666"\
"7......6777777777777777666665..56667..666666"\
"7..77..5...............6666665..5667.6666666"\
"67776..5...............66666665..767.6666666"\
"6666...5..8.........8..666666665.567.6666666"\
"6....8.5...............66666666...67..666666"\
"6.66.8.5...............6666667.....7...66666"\
"666....5.....33.33.....6666668...........666"\
"66...665.....3...3.....6666667.....7576...66"\
"6...6665.....3...3.....66666666...66666...66"\
"6.666665.....3...3.....666666665.5655666.666"\
"6.66...5.....33333.....6665666.5.55.5666.666"\
"6.6....5...............655.556......5666.666"\
"6.6....5...............6.....55...556666.666"\
"6......5..8.........8..6..........66666...66"\
"6555...5...............6.....66...566666.666"\
"65.....5...............655.55666...56666.666"\
"65.....56666668.8666666665.5666656566666.666"\
"65...6666666668.8666666555.5555555566666.666"\
"5...66666666668.8666666...........56666...66"\
"7.7666666666668.8666666...........5666.....6"\
"7.7666666666668.8666666............566.....6"\
"7.7666666666668.8666666............566.....6"\
"7.76........666.6666666............5666...66"\
"7.76...88888666.666666655555.......56666.666"\
"7.73...8...8666.6666666....5..............66"\
"7..........8666.6666666....5.......56668.866"\
"7773...8...8666.6666666....5.......5668...86"\
"6676...88888666............3.......568.....8"\
"6676........66666666665...........7668.....8"\
"66666666666666666666665555537777776668.....8"\
"666666666666666666666666666666666666668...86"\
"66666666666666666666666666666666666666688866"
ceilingTex = "=="
ceilingColor = 1
ceilingIsBright = 1
floorTex = "__"
floorColor = 4
floorIsBright = 0
monsterTex = "OOMMZZ[]FuLL"
monsterColor = 2
bulletTex = "**"
bulletColor = 3
bulletIsBright = 0
wallTex="kkrrggyybbmmccww"
#initial player position
posX = 37.5
posY = 9.5
for (i=0; i < 25; i++)
spawnMonster()
#ENTERING MAIN LOOP
main()
#EXITING
print "\n"
if(health <= 0)
print "GAME OVER! YOU LOSE!"
else if (moves == -1)
print "YOU WIN! YOUR SCORE: " score
else
print "You quit. Progress was not saved."
print "Credits: Fedor 'TheMozg' Kalugin"
print "https://github.com/TheMozg/awk-raycaster"
print "Gameplay testing - Alex 'Yakojo' & Danya 'bogych97'"
print "Go away!"
}
function addSprite(x,y, dX,dY, tex, color, isBright, type, uDiv, vDiv, vMove) {
n = length(sprite)+1
sprite[n]["dirX"]=dX
sprite[n]["dirY"]=dY
sprite[n]["posX"]=x
sprite[n]["posY"]=y
sprite[n]["tex"]=tex
sprite[n]["color"]=color
sprite[n]["isBright"]=isBright
sprite[n]["type"]=type
sprite[n]["vDiv"]=vDiv
sprite[n]["uDiv"]=uDiv
sprite[n]["vMove"]=vMove
}
function spawnMonster(){
do{
x = mapWidth*rand()
y = mapHeight*rand()
} while ((worldMap(x, y) != 0) || (distPP(x,y,posX,posY) < 10))
isBright = int(2*rand())
n = int(rand()*int(length(monsterTex)/2))+1
tex = substr(monsterTex, n*2-1, 2)
addSprite(x, y, dirX, dirY, tex, monsterColor, isBright, "monster", 1.0, 1.0, 0.0)
}
function shoot() {
addSprite(posX, posY, dirX, dirY, bulletTex, bulletColor, bulletIsBright, "bullet", 3.0, 3.0, 0)
n = length(sprite)
moveSprite(n, 0.1)
}
function worldMap(y, x) {
y = int(y)
x = int(x)
tile = substr(map, mapWidth*y+x+1, 1)
if (tile == ".")
return 0
return tile
}
function abs(x) {
if(x<0)
return -x
return x
}
function fillBackground(){
for(x = 0; x < w; x++){
for(y = 0; y < h/2; y++){
buffer[x,y] = getPixel(ceilingColor, ceilingIsBright, colormode, ceilingTex);
}
for(y = int(h/2); y < h; y++){
buffer[x,y] = getPixel(floorColor, floorIsBright, colormode, floorTex);
}
}
}
function redraw(){
printf "\033[H"
for(y = 0; y < h-2; y++){
str = ""
for(x = 0; x < w; x++){
str = str buffer[x,y]
}
print str
}
drawUI()
printf "\033[J"
}
function drawUI(){
if(colormode == 1 || colormode == 2){
fg_color = getANSICode(0, 0, 0);
bg_color = getANSICode(0, 0, 1);
}
if(colormode == 3 || colormode == 4){
fg_color = getANSICode(8, 1, 0);
bg_color = getANSICode(5, 0, 1);
}
help = toupper(MOVF_KEY)\
toupper(MOVL_KEY)\
toupper(MOVB_KEY)\
toupper(MOVR_KEY)\
" - move"
help = help ", " toupper(ROTL_KEY) "/" toupper(ROTR_KEY)\
"- turn left/right (shift = quicker)"
if(FIRE_KEY == " ")
help = help ", " "spacebar" " - shoot"
else
help = help ", " toupper(FIRE_KEY) " - shoot"
help = help ", 1-4 - change color mode"
info = "ELEVATOR COMING " moves " | HP " health " | SCORE " score " | GUN "
if(reloadTimeLeft == 0)
info = info "READY"
else
info = info "RELOADING"
if(inPosition())
if (moves != 0)
info = info " | WAIT FOR ELEVATOR"
else
info = info " | PRESS " toupper(UWIN_KEY) " TO WIN"
else
info = info " | find an elevator and press " toupper(UWIN_KEY)
while(length(help) < w*2)
help = help " "
while(length(info) < w*2)
info = info " "
print buildPixel(bg_color, fg_color, info)
print buildPixel(bg_color, fg_color, help)
}
function getWallTex(color, isBright){
tex = substr(wallTex, color*2-1, 2)
if(isBright == 1)
return toupper(tex)
return tex
}
function getANSICode(color, isBright, isBG){
if(color == 0)
color = 10
else if (isBright==1)
color+=60
if(isBG==1)
color+=10
color+=30-1
return color
}
function buildPixel(bg_color, fg_color, text){
pixel = "\033[" bg_color ";" fg_color "m" text "\033[0m";
return pixel;
}
function getPixel(basecolor, isBright, colormode, tex){
color = "??";
if (colormode==1) {
color = tex;
}
else if (colormode==2) {
fg_color = getANSICode(basecolor, isBright, 0);
bg_color = getANSICode(0, isBright, 1);
color = buildPixel(bg_color, fg_color, tex);
}
else if (colormode==3) {
tex = " ";
fg_color = getANSICode(0, isBright, 0);
bg_color = getANSICode(basecolor, isBright, 1);
color = buildPixel(bg_color, fg_color, tex);
}
else if (colormode==4){
bg_color = getANSICode(basecolor, isBright, 1);
if (isBright == 0)
isBright = 1;
else
isBright = 0;
fg_color = getANSICode(basecolor, isBright, 0);
color = buildPixel(bg_color, fg_color, tex);
}
return color;
}
function distSP(i, x, y){
return distPP(sprite[i]["posX"], sprite[i]["posY"], x, y)
}
function distSS(i, j){
return distPP(sprite[i]["posX"], sprite[i]["posY"], sprite[j]["posX"], sprite[j]["posY"])
}
function distPP(x1, y1, x2, y2){
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))
}
function inPosition(){
if(posX <= 22 && posX >= 19.5 && posY >= 13 && posY <= 17)
return 1
return 0
}
function moveSprite(n, speed) {
newPosX = sprite[n]["posX"]+sprite[n]["dirX"]*speed
newPosY = sprite[n]["posY"]+sprite[n]["dirY"]*speed
if(worldMap(newPosX,sprite[n]["posY"]) == 0)
sprite[n]["posX"] = newPosX
if(worldMap(sprite[n]["posX"],newPosY) == 0)
sprite[n]["posY"] = newPosY
return (worldMap(newPosX,newPosY) == 0)
}
function compareSprites(i1, v1, i2, v2){
return (v2["dist"] - v1["dist"])
}
function main()
{
while (1) {
if(moves != 0)
moves--
if(reloadTimeLeft != 0)
reloadTimeLeft--
if(health <= 0)
break;
fillBackground();
for(x = 0; x < w; x++)
{
#calculate ray position and direction
cameraX = 2 * x / w - 1; #x-coordinate in camera space
rayPosX = posX;
rayPosY = posY;
rayDirX = dirX + planeX * cameraX;
rayDirY = dirY + planeY * cameraX;
#which box of the map we're in
mapX = int(rayPosX);
mapY = int(rayPosY);
#length of ray from current position to next x or y-side
sideDistX=0.0;
sideDistY=0.0;
#length of ray from one x or y-side to next x or y-side
if(rayDirX != 0)
deltaDistX = sqrt(1 + (rayDirY * rayDirY) / (rayDirX * rayDirX));
else
deltaDistX=999999;
if(rayDirY != 0)
deltaDistY = sqrt(1 + (rayDirX * rayDirX) / (rayDirY * rayDirY));
else
deltaDistY=999999;
perpWallDist=0.0;
#what direction to step in x or y-direction (either +1 or -1)
stepX=0;
stepY=0;
hit = 0; #was there a wall hit?
side = 0; #was a NS or a EW wall hit?
#calculate step and initial sideDist
if (rayDirX < 0) {
stepX = -1;
sideDistX = (rayPosX - mapX) * deltaDistX;
}
else {
stepX = 1;
sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX;
}
if (rayDirY < 0) {
stepY = -1;
sideDistY = (rayPosY - mapY) * deltaDistY;
}
else {
stepY = 1;
sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY;
}
#perform DDA
while (hit == 0) {
#jump to next map square, OR in x-direction, OR in y-direction
if (sideDistX < sideDistY) {
sideDistX += deltaDistX;
mapX += stepX;
side = 0;
}
else{
sideDistY += deltaDistY;
mapY += stepY;
side = 1;
}
#Check if ray has hit a wall
if (worldMap(mapX,mapY) > 0)
hit = 1;
}
#Calculate distance projected on camera direction
if (side == 0)
perpWallDist = abs( (mapX - rayPosX + int((1 - stepX) / 2)) / rayDirX);
else
perpWallDist = abs( (mapY - rayPosY + int((1 - stepY) / 2)) / rayDirY);
#Calculate height of line to draw on screen
if(perpWallDist == 0)
lineHeight = h
else
lineHeight = abs(int(h / perpWallDist));
#calculate lowest and highest pixel to fill in current stripe
drawStart = int(int(h / 2)-int(lineHeight / 2) );
if(drawStart < 0) drawStart = 0;
drawEnd = int(lineHeight / 2 + h / 2);
if(drawEnd >= h) drawEnd = h - 1;
#choose wall color
tex = getWallTex(worldMap(mapX,mapY), side);
color = getPixel(worldMap(mapX,mapY), side, colormode, tex);
#draw the pixels of the stripe as a vertical line
for(y = drawStart; y <= drawEnd; y++) {
buffer[x,y] = color
}
#set ZBuffer for sprite casting
ZBuffer[x] = perpWallDist; #perpendicular distance is used
}
#sort sprites from far to close
for(i in sprite) {
sprite[i]["dist"] = distSP(i, posX, posY)
}
asort(sprite, sprite, "compareSprites")
#after sorting the sprites, do the projection and draw them
for(i in sprite)
{
#translate sprite position to relative to camera
spriteX = sprite[i]["posX"] - posX;
spriteY = sprite[i]["posY"] - posY;
#transform sprite with the inverse camera matrix
#required for correct matrix multiplication
invDet = 1.0 / (planeX * dirY - dirX * planeY);
transformX = invDet * (dirY * spriteX - dirX * spriteY);
#this is actually the depth inside the screen, that what Z is in 3D
transformY = invDet * (-planeY * spriteX + planeX * spriteY);
spriteScreenX = int((w / 2) * (1 + transformX / transformY));
#controls moving the sprite up or down
vMoveScreen = int(sprite[i]["vMove"] / transformY);
#calculate height of the sprite on screen
#using "transformY" instead of the real distance prevents fisheye
spriteHeight = abs(int((h / transformY) / sprite[i]["vDiv"]));
#calculate lowest and highest pixel to fill in current stripe
drawStartY = int(int(-spriteHeight/2) + h/2 + vMoveScreen);
if(drawStartY < 0) drawStartY = 0;
drawEndY = int(int(spriteHeight / 2) + h/2 + vMoveScreen);
if(drawEndY >= h) drawEndY = h - 1;
#calculate width of the sprite
spriteWidth = abs(int((h /transformY) / sprite[i]["uDiv"]));
drawStartX = int(spriteScreenX-int(spriteWidth / 2));
if(drawStartX < 0) drawStartX = 0;
drawEndX = int(int(spriteWidth / 2) + spriteScreenX);
if(drawEndX >= w) drawEndX = w - 1;
#loop through every vertical stripe of the sprite on screen
for(stripe = drawStartX; stripe <= drawEndX; stripe++){
if(transformY > 0 && stripe >= 0 && stripe < w && transformY < ZBuffer[stripe])
for(y = drawStartY; y <= drawEndY; y++){ #for every pixel of the current stripe
draw as circle
if((stripe-spriteScreenX)*(stripe-spriteScreenX)+(y-h/2)*(y-h/2) <= spriteHeight*spriteHeight/4){
pixel = getPixel(sprite[i]["color"], sprite[i]["isBright"], colormode, sprite[i]["tex"]);
buffer[stripe,y] = pixel;
}
}
}
}
redraw();
system("stty -echo")
#avoids depending on bash and gawk
#by izabera from #bash on freenode
cmd = "saved=$(stty -g); stty raw; var=$(dd bs=1 count=1 2>/dev/null); stty \"$saved\"; echo \"$var\""
cmd | getline input
close(cmd)
system("stty echo")
if (input == MOVF_KEY || input == MOVB_KEY || input == MOVL_KEY || input == MOVR_KEY){
newPosX = posX - dirX * moveSpeed
newPosY = posY - dirY * moveSpeed
if(input == MOVF_KEY){
newPosX = posX + dirX * moveSpeed
newPosY = posY + dirY * moveSpeed
}
if(input == MOVL_KEY){
newPosX = posX - dirY * moveSpeed
newPosY = posY + dirX * moveSpeed
}
if(input == MOVR_KEY){
newPosX = posX + dirY * moveSpeed
newPosY = posY - dirX * moveSpeed
}
ok = 1;
for(i in sprite) {
dist = distSP(i, newPosX, newPosY);
if(dist < 0.51 && sprite[i]["type"] == "monster")
ok = 0;
}
if(ok){
if(worldMap(newPosX,posY) == 0) posX = newPosX;
if(worldMap(posX,newPosY) == 0) posY = newPosY;
}
}
if (input == ROTL_KEY ||
input == ROTR_KEY ||
input == toupper(ROTL_KEY) ||
input == toupper(ROTR_KEY)){
rot = rotSpeed
if(input == toupper(ROTL_KEY) || input == toupper(ROTR_KEY))
rot = rot*2
if (input == ROTR_KEY || input == toupper(ROTR_KEY))
rot = -rot
#both camera direction and camera plane must be rotated
oldDirX = dirX
dirX = dirX * cos(rot) - dirY * sin(rot)
dirY = oldDirX * sin(rot) + dirY * cos(rot)
oldPlaneX = planeX
planeX = planeX * cos(rot) - planeY * sin(rot)
planeY = oldPlaneX * sin(rot) + planeY * cos(rot)
}
if(input == FIRE_KEY && reloadTimeLeft == 0){
shoot()
reloadTimeLeft = reloadTime
}
if(input == "1")
colormode = 1
if(input == "2")
colormode = 2
if(input == "3")
colormode = 3
if(input == "4")
colormode = 4
if(input == EXIT_KEY)
break
if(input == UWIN_KEY && moves == 0 && inPosition()){
moves = -1
break
}
spawnCount = 0
for(i in sprite){
if(!(i in sprite))
continue
if (sprite[i]["type"] == "monster"){
d = distSP(i, posX, posY)
sprite[i]["dirX"] = (posX - sprite[i]["posX"]) / d
sprite[i]["dirY"] = (posY - sprite[i]["posY"]) / d
x = sprite[i]["posX"]+sprite[i]["dirX"]*0.5
y = sprite[i]["posY"]+sprite[i]["dirY"]*0.5
if(d > 0.7){
#prevent clustering of monsters
ok = 1
for(j in sprite){
if(!(j in sprite))
continue
if(sprite[j]["type"] == "monster" && i != j && distSP(j,x,y) < 1)
ok = 0;
}
if(ok)
moveSprite(i, 0.5)
}
else{
health -= 10
delete sprite[i]
spawnCount++
}
}
}
for(i in sprite){
if(!(i in sprite))
continue
if (sprite[i]["type"] == "bullet"){
for(j in sprite){
if(!(j in sprite))
continue
if (sprite[j]["type"] == "monster"){
if(distSS(i,j) < 1){
delete sprite[j]
delete sprite[i]
score += 100
reloadTimeLeft = 0
spawnCount++
break
}
}
}
if(i in sprite)
if(!moveSprite(i, 1.2))
delete sprite[i]
}
}
for (i=0; i < spawnCount*3; i++) {
spawnMonster()
}
}
}