const sf = struct { pub usingnamespace @import("sfml"); pub usingnamespace graphics; pub usingnamespace window; }; const isKeyPressed = sf.window.keyboard.isKeyPressed; const std = @import("std"); usingnamespace @import("raycast.zig"); usingnamespace @import("renderConstants.zig"); usingnamespace @import("map.zig"); const screenWidth = 1280; const screenHeight = 960; fn renderWall( window: sf.graphics.RenderWindow, sprite: sf.graphics.Sprite, col: i32, // which column top: f32, // top of wall height: f32, // how tall texfrac: f32, // how far along the texture texture: u8, // which texture index ) !void { const hFact = screenWidth / planeWidth; const vFact = screenHeight / planeHeight; const xpos = @intToFloat(f32, col) * hFact; const length = height * vFact; const starty = (screenHeight + vFact * planeHeight) / 2 - top * vFact; const tind = @as(c_int, texture) * @floatToInt(c_int, 1 + wallTextureDim); sprite.setPosition(.{ .x = xpos, .y = starty }); sprite.setScale(.{ .x = hFact, .y = length / wallTextureDim }); sprite.setTextureRect(.{ .top = 0, .left = tind + @floatToInt(c_int, texfrac * wallTextureDim), .width = 1, .height = wallTextureDim }); window.draw(sprite, null); } pub fn main() !void { const PI = std.math.pi; // Setup player and map //-------------------------------------------------------------------------- var player = Player.new(15.75, 1.5, // pos PI / 2.0, // ang @intToFloat(f32, planeWidth), @intToFloat(f32, planeHeight)); var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); var map = try Map.new(32, 32, &gpa.allocator); defer map.deinit(); var y: u32 = 0; while (y < 32) : (y += 1) { map.cells.items[y * 32 + 00] = Cell{ .height = MAX_HEIGHT }; map.cells.items[y * 32 + 31] = Cell{ .height = MAX_HEIGHT }; map.cells.items[31 * 32 + y] = Cell{ .height = MAX_HEIGHT }; map.cells.items[00 * 32 + y] = Cell{ .height = MAX_HEIGHT }; } map.cells.items[y * 15 + 15] = Cell{ .height = MAX_HEIGHT, .wall_texture = 1 }; map.cells.items[y * 15 + 14] = Cell{ .height = MAX_HEIGHT / 1.5, .wall_texture = 1 }; // Initialise SFML //-------------------------------------------------------------------------- var window = try sf.RenderWindow.create(.{ .x = screenWidth, .y = screenHeight }, 32, "zirc", sf.window.Style.none); defer window.destroy(); window.setFramerateLimit(30); var backRect = try sf.RectangleShape.create(.{ .x = screenWidth, .y = screenHeight / 2 }); defer backRect.destroy(); backRect.setPosition(.{ .x = 0, .y = screenHeight / 2 }); backRect.setFillColor(sf.Color.fromRGB(50, 50, 50)); // Load the background skybox, really `skycylinder', and assign it to a // sprite -- it's set to repeating so that it goes on forever. We need to // scale the sprite so that drawing 1/4 of backTextureWidth fills the screen // horizontally, and drawing backTextureHeight fills half of the screen // vertically. var backTexture = try sf.Texture.createFromFile("back.png"); defer backTexture.destroy(); backTexture.setSmooth(false); // looks worse => better! backTexture.setRepeated(true); var backSprite = try sf.Sprite.createFromTexture(backTexture); defer backSprite.destroy(); backSprite.setScale(.{ .x = 4.0 * @intToFloat(f32, screenWidth) / backTextureWidth, .y = @intToFloat(f32, screenHeight) / (2 * backTextureHeight) }); // Load brick texture! var wallTexture = try sf.Texture.createFromFile("walls.png"); defer wallTexture.destroy(); wallTexture.setSmooth(false); // looks worse => better! var wallSprite = try sf.Sprite.createFromTexture(wallTexture); defer wallSprite.destroy(); // Start your engines! //-------------------------------------------------------------------------- while (window.isOpen()) { while (window.pollEvent()) |event| { if (event == .closed) window.close(); } if (isKeyPressed(.Escape)) window.close(); const rotation = 2.0 * PI / 100.0; if (isKeyPressed(.R)) player.ang += rotation; if (isKeyPressed(.T)) player.ang -= rotation; player.acc_x = 0; player.acc_y = 0; const acc_mag = 50; if (isKeyPressed(.Right)) { player.acc_x += std.math.cos(player.ang - PI / 2.0) * acc_mag; player.acc_y += std.math.sin(player.ang - PI / 2.0) * acc_mag; } if (isKeyPressed(.Left)) { player.acc_x -= std.math.cos(player.ang - PI / 2.0) * acc_mag; player.acc_y -= std.math.sin(player.ang - PI / 2.0) * acc_mag; } if (isKeyPressed(.Up) or isKeyPressed(.F)) { player.acc_x += std.math.cos(player.ang) * acc_mag; player.acc_y += std.math.sin(player.ang) * acc_mag; } if (isKeyPressed(.Down) or isKeyPressed(.S)) { player.acc_x -= std.math.cos(player.ang) * acc_mag; player.acc_y -= std.math.sin(player.ang) * acc_mag; } window.clear(sf.Color.fromRGB(100, 100, 100)); const back_scroll = @floatToInt(i32, -backTextureWidth * player.ang / (2 * PI)); backSprite.setTextureRect(sf.IntRect{ .left = back_scroll, // fix location of texture in the sky .top = 0, // scaling ensures that the next two paint it across the width of // the screen, and for half the height of the screen .width = backTextureWidth / 4.0, // 1/4 of texture per screen .height = backTextureHeight, // full height of texture visible }); window.draw(backSprite, null); try player.renderMapUsing(window, wallSprite, map, renderWall); window.display(); player.tick(); } }