(local pxl8 (require :pxl8)) (local effects (require :pxl8.effects)) (local net (require :pxl8.net)) (local shader (require :pxl8.shader)) (local colormap (require :mod.colormap)) (local entities (require :mod.entities)) (local menu (require :mod.menu)) (local palette (require :mod.palette)) (local sky (require :mod.sky)) (local textures (require :mod.textures)) (local bob-amount 4.0) (local bob-speed 8.0) (local cam-smoothing 0.25) (local ceiling-height 120) (local chunk-size 64) (local cursor-sensitivity 0.010) (local gravity 600) (local grid-size 64) (local jump-velocity 180) (local land-recovery-speed 20) (local land-squash-amount -4) (local max-pitch 1.5) (local move-speed 200) (local player-eye-height 64) (local player-height 72) (local player-radius 12) (local step-height 24) (local turn-speed 4.0) (var auto-run-cancel-key nil) (var auto-run? false) (var bob-time 0) (var cam-pitch 0) (var cam-x 416) (var cam-y 0) (var cam-yaw 0) (var cam-z 416) (var camera nil) (var ceiling-tex nil) (var floor-tex nil) (var fps-avg 0) (var fps-sample-count 0) (var grounded true) (var land-squash 0) (var last-dt 0.016) (var light-time 0) (var lights nil) (var bsp-materials-setup false) (var network nil) (var portal-cooldown 0) (var real-time 0) (var smooth-cam-x 416) (var smooth-cam-z 416) (var vel-y 0) (var trim-tex nil) (var wall-tex nil) (var world nil) (local cursor-look? true) (local MOSS_COLOR 200) (local PLASTER_COLOR 16) (local STONE_FLOOR_START 37) (local STONE_WALL_START 2) (local WOOD_COLOR 88) (fn find-floor [x y z in-bsp] (if (not world) 0 (if in-bsp 0 (let [start-y (math.max (+ y 64) 256) ray (world:ray x start-y z x (- y 1000) z)] (if ray.hit (+ ray.point.y 1) 128))))) (fn find-ceiling [x y z max-height] (if (not world) nil (let [ray (world:ray x (+ y 1) z x (+ y max-height) z)] (if ray.hit (- ray.point.y 1) nil)))) (fn preload [] (when (not network) (set network (net.get)) (when network (network:spawn cam-x cam-y cam-z cam-yaw cam-pitch))) (when (not world) (set world (pxl8.get_world)) (when world (world:init_local_player cam-x cam-y cam-z)))) (fn is-connected [] (and network (network:connected))) (fn is-ready [] (if (not world) false (let [chunk (world:active_chunk)] (and chunk (chunk:ready))))) (fn init [] (pxl8.set_relative_mouse_mode true) (pxl8.set_palette palette 256) (pxl8.set_colormap colormap 16384) (for [i 0 7] (let [t (/ i 7) r 0xFF g (math.floor (+ 0x60 (* t (- 0xE0 0x60)))) b (math.floor (+ 0x10 (* t (- 0x80 0x10))))] (pxl8.set_palette_rgb (+ entities.FIREBALL_COLOR i) r g b))) (pxl8.set_palette_rgb (- entities.FIREBALL_COLOR 1) 0xFF 0x20 0x10) (sky.reset-gradient) (sky.update-gradient 1 2 6 6 10 18) (pxl8.update_palette_deps) (when (not camera) (set camera (pxl8.create_camera_3d))) (when (not lights) (set lights (pxl8.create_lights))) (entities.init textures) (sky.generate-stars 12345) (preload) (when (not ceiling-tex) (set ceiling-tex (textures.plaster-wall 66666 PLASTER_COLOR))) (when (not floor-tex) (set floor-tex (textures.wood-planks 44444 WOOD_COLOR))) (when (not trim-tex) (set trim-tex (textures.wood-trim 77777 WOOD_COLOR))) (when (not wall-tex) (set wall-tex (textures.cobble-timber 55555 STONE_WALL_START MOSS_COLOR WOOD_COLOR))) ) (fn setup-materials [] (when (and world (not bsp-materials-setup) ceiling-tex floor-tex trim-tex wall-tex) (let [chunk (world:active_chunk)] (when (and chunk (chunk:ready)) (let [ceiling-mat (pxl8.create_material {:texture ceiling-tex}) floor-mat (pxl8.create_material {:texture floor-tex :lighting true}) trim-mat (pxl8.create_material {:texture trim-tex :lighting true}) wall-mat (pxl8.create_material {:texture wall-tex :lighting true})] (world:set_bsp_material 0 floor-mat) (world:set_bsp_material 1 wall-mat) (world:set_bsp_material 2 ceiling-mat) (world:set_bsp_material 3 trim-mat) (set bsp-materials-setup true)))))) (fn sample-input [] (var move-forward 0) (var move-right 0) (when (pxl8.key_pressed "`") (set auto-run? (not auto-run?)) (when (and auto-run? (pxl8.key_down "w")) (set auto-run-cancel-key "w"))) (when (and auto-run? (not auto-run-cancel-key) (or (pxl8.key_down "w") (pxl8.key_down "s"))) (set auto-run? false) (when (pxl8.key_down "s") (set auto-run-cancel-key "s"))) (when (and auto-run-cancel-key (not (pxl8.key_down auto-run-cancel-key))) (set auto-run-cancel-key nil)) (when (or (pxl8.key_down "w") auto-run?) (set move-forward (+ move-forward 1))) (when (and (pxl8.key_down "s") (not= auto-run-cancel-key "s")) (set move-forward (- move-forward 1))) (when (pxl8.key_down "a") (set move-right (- move-right 1))) (when (pxl8.key_down "d") (set move-right (+ move-right 1))) {:move_x move-right :move_y move-forward :look_dx (pxl8.mouse_dx) :look_dy (pxl8.mouse_dy)}) (fn update [dt] (set last-dt dt) (let [fps (pxl8.get_fps)] (set fps-sample-count (+ fps-sample-count 1)) (set fps-avg (+ (* fps-avg (/ (- fps-sample-count 1) fps-sample-count)) (/ fps fps-sample-count))) (when (>= fps-sample-count 120) (set fps-sample-count 0) (set fps-avg 0))) (setup-materials) (when (> portal-cooldown 0) (set portal-cooldown (- portal-cooldown dt))) (when (and network (<= portal-cooldown 0)) (let [chunk (world:active_chunk) in-bsp (not= chunk nil) (door-x door-z) (entities.get-door-position) door-radius (entities.get-door-radius) dist-to-door (math.sqrt (+ (* (- cam-x door-x) (- cam-x door-x)) (* (- cam-z door-z) (- cam-z door-z))))] (when (< dist-to-door door-radius) (if in-bsp (do (pxl8.info "Exiting through door...") (let [exit-x (+ door-x 50) exit-z door-z exit-y 200] (network:exit_chunk exit-x exit-y exit-z) (set cam-x exit-x) (set cam-z exit-z) (set cam-y exit-y)) (set vel-y 0) (set grounded false) (set smooth-cam-x cam-x) (set smooth-cam-z cam-z) (set bsp-materials-setup false) (set portal-cooldown 2.0)) (do (pxl8.info "Entering through door...") (network:enter_chunk 1) (set cam-x 416) (set cam-z 416) (set cam-y 0) (set vel-y 0) (set grounded true) (set smooth-cam-x 416) (set smooth-cam-z 416) (set portal-cooldown 2.0)))))) (let [chunk (world:active_chunk) chunk-id (if network (network:chunk_id) -1) expecting-bsp (> chunk-id 0) voxel-space (and (not chunk) (= chunk-id 0)) ready (or voxel-space (and chunk (chunk:ready)))] (when ready (let [input (sample-input) grid-max (if voxel-space 100000 (* grid-size chunk-size)) movement-yaw cam-yaw] (let [player (world:local_player)] (when player (set cam-x player.pos.x) (set cam-y player.pos.y) (set cam-z player.pos.z) (set cam-yaw player.yaw) (set cam-pitch player.pitch))) (when (and voxel-space grounded) (let [floor-y (find-floor cam-x cam-y cam-z false) height-diff (- floor-y cam-y)] (if (and (>= height-diff (- step-height)) (<= height-diff step-height)) (let [lerp-speed 0.3 smooth-y (+ (* cam-y (- 1 lerp-speed)) (* floor-y lerp-speed))] (set cam-y smooth-y)) (when (< height-diff (- step-height)) (set grounded false))))) (set smooth-cam-x (+ (* smooth-cam-x (- 1 cam-smoothing)) (* cam-x cam-smoothing))) (set smooth-cam-z (+ (* smooth-cam-z (- 1 cam-smoothing)) (* cam-z cam-smoothing))) (when (and (pxl8.key_pressed "space") grounded) (set vel-y jump-velocity) (set grounded false)) (when (or (not grounded) (not= vel-y 0)) (set vel-y (- vel-y (* gravity dt))) (if (> vel-y 0) (let [new-y (+ cam-y (* vel-y dt)) head-y (+ new-y player-height) ceiling-y (if voxel-space (find-ceiling cam-x cam-y cam-z 200) ceiling-height)] (if (and ceiling-y (>= head-y ceiling-y)) (do (set cam-y (- ceiling-y player-height)) (set vel-y 0)) (set cam-y new-y))) (let [new-y (+ cam-y (* vel-y dt)) floor-y (find-floor cam-x cam-y cam-z (not voxel-space))] (if (<= new-y floor-y) (do (set cam-y floor-y) (set land-squash land-squash-amount) (set vel-y 0) (set grounded true)) (do (set cam-y new-y) (set grounded false)))))) (when (< land-squash 0) (set land-squash (math.min 0 (+ land-squash (* land-recovery-speed dt))))) (let [moving (or (not= input.move_x 0) (not= input.move_y 0))] (if (and moving grounded) (set bob-time (+ bob-time (* dt bob-speed))) (let [target-phase (* (math.floor (/ bob-time math.pi)) math.pi)] (set bob-time (+ (* bob-time 0.8) (* target-phase 0.2)))))) (set light-time (+ light-time (* dt 0.5))) (set real-time (+ real-time dt)))))) (fn frame [] (pxl8.clear 1) (when (not camera) (pxl8.error "camera is nil!")) (when (not world) (pxl8.error "world is nil!")) (let [chunk (when world (world:active_chunk)) expecting-bsp (and network (> (network:chunk_id) 0)) voxel-space (and world (not chunk) (not expecting-bsp)) ready (or voxel-space (and chunk (chunk:ready)))] (when (and world (not ready)) (pxl8.text "Waiting for world data..." 5 30 12)) (when (and camera world ready) (let [bob-offset (* (math.sin bob-time) bob-amount) eye-y (+ cam-y player-eye-height bob-offset land-squash) forward-x (- (math.sin cam-yaw)) forward-z (- (math.cos cam-yaw)) target-x (+ smooth-cam-x forward-x) target-y (+ eye-y (math.sin cam-pitch)) target-z (+ smooth-cam-z forward-z) aspect (/ (pxl8.get_width) (pxl8.get_height))] (camera:lookat [smooth-cam-x eye-y smooth-cam-z] [target-x target-y target-z] [0 1 0]) (camera:set_perspective 1.047 aspect 1.0 4096.0) (let [light-x (+ 384 (* 50 (math.cos light-time))) light-z (+ 324 (* 50 (math.sin light-time))) light-y 80 phase (+ (* light-x 0.01) 1.7) f1 (* 0.08 (math.sin (+ (* real-time 2.5) phase))) f2 (* 0.05 (math.sin (+ (* real-time 4.1) (* phase 0.7)))) f3 (* 0.03 (math.sin (+ (* real-time 7.3) (* phase 1.2)))) flicker (+ 0.92 f1 f2 f3) light-intensity (math.floor (math.max 0 (math.min 255 (* 255 flicker)))) r1 (* 0.06 (math.sin (+ (* real-time 1.8) (* phase 0.5)))) r2 (* 0.04 (math.sin (+ (* real-time 3.2) phase))) light-radius (* 150 (+ 0.95 r1 r2))] (lights:clear) (lights:add light-x light-y light-z 0xFF8C32 light-intensity light-radius) (pxl8.begin_frame_3d camera lights { :ambient 30 :fog_density 0.0 :celestial_dir [0.5 -0.8 0.3] :celestial_intensity 0.5}) (pxl8.clear_depth) (sky.update-gradient 1 2 6 6 10 18) (sky.render smooth-cam-x eye-y smooth-cam-z (menu.is-wireframe)) (pxl8.clear_depth) (world:set_wireframe (menu.is-wireframe)) (world:render [smooth-cam-x eye-y smooth-cam-z]) (when chunk (entities.render-fireball light-x light-y light-z (menu.is-wireframe))) (entities.render-door (menu.is-wireframe) (if voxel-space 128 0)) (pxl8.end_frame_3d)) ;; TODO: shader needs to run at present time, not mid-frame ;; (shader.begin_frame) ;; (shader.run shader.presets.light_with_fog ;; {:fog_r 6 :fog_g 6 :fog_b 12 ;; :fog_start 0.1 :fog_end 0.85}) (sky.render-stars smooth-cam-x eye-y smooth-cam-z 1.0 last-dt) (let [cx (/ (pxl8.get_width) 2) cy (/ (pxl8.get_height) 2) crosshair-size 4 crosshair-color 240 text-color 251] (pxl8.line (- cx crosshair-size) cy (+ cx crosshair-size) cy crosshair-color) (pxl8.line cx (- cy crosshair-size) cx (+ cy crosshair-size) crosshair-color) (pxl8.text (.. "fps: " (string.format "%.0f" fps-avg)) 5 5 text-color) (pxl8.text (.. "pos: " (string.format "%.0f" cam-x) "," (string.format "%.0f" cam-y) "," (string.format "%.0f" cam-z)) 5 15 text-color)))))) {:preload preload :is-connected is-connected :is-ready is-ready :init init :update update :frame frame}