Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign up
Fetching contributors…
| #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() | |
| } | |
| } | |
| } |