From b174b8c00026253fd40ec262e430b0bb764e31ea Mon Sep 17 00:00:00 2001 From: Alexander M Pickering Date: Sat, 25 Jan 2025 20:40:09 -0600 Subject: work --- src/ambient.moon | 1 + src/bobber.moon | 78 ++++++++++++ src/color.moon | 23 +++- src/controllers/fish.moon | 54 ++++++++ src/controllers/mouse_keyboard.moon | 136 +++++++++++++++++++-- src/ecs.moon | 14 +++ src/islandgen.moon | 153 ++++++++++++++++------- src/main.lua | 9 +- src/player.moon | 238 +++++++++++++++++++++++++++--------- src/prefab/cabin.moon | 63 ++++++++++ src/preload.lua | 24 +++- src/router.moon | 6 + src/settings.moon | 11 ++ src/shaders/lake.frag | 92 +++++++++++++- src/shaders/land.frag | 72 +++++++++-- src/shaders/land.vert | 16 ++- src/shaders/stars.frag | 5 + src/shaders/stars.lua | 77 ++++++------ src/shaders/stars.vert | 30 +++++ src/spawn_fish.moon | 130 ++++++++++++++++++++ src/test_entity.moon | 36 ++++++ src/window.moon | 4 +- src/world.moon | 149 +++++++++++++++++++++- src/worldgen.moon | 8 +- 24 files changed, 1243 insertions(+), 186 deletions(-) create mode 100644 src/ambient.moon create mode 100644 src/bobber.moon create mode 100644 src/controllers/fish.moon create mode 100644 src/prefab/cabin.moon create mode 100644 src/settings.moon create mode 100644 src/shaders/stars.frag create mode 100644 src/shaders/stars.vert create mode 100644 src/spawn_fish.moon create mode 100644 src/test_entity.moon (limited to 'src') diff --git a/src/ambient.moon b/src/ambient.moon new file mode 100644 index 0000000..e99dae8 --- /dev/null +++ b/src/ambient.moon @@ -0,0 +1 @@ +-- frog croak: 35895209 diff --git a/src/bobber.moon b/src/bobber.moon new file mode 100644 index 0000000..6f52c6b --- /dev/null +++ b/src/bobber.moon @@ -0,0 +1,78 @@ +world = require("world") +sprites = require("world.sprites") +ecs = require("ecs") + +class BobberGraphicsComponent extends world.GraphicsComponent + @loctbl = { + {-1,-1} + {-1,1} + {1,1} + {1,1} + {1,-1} + {-1,-1} + } + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + @buf = geom_view + h = 0.1 + uv = sprites.player_normal + utbl = { + [-1]: {uv.s1, uv.t1} + [1]: {uv.s2, uv.t2} + } + z = -0.1 + print("Bobber created at", @properties.pos) + for i = 1,6 + loctbl = @@loctbl[i] + geom_view[i] = vec3(@@loctbl[i][1] * h, @@loctbl[i][2] * h, z) + vec3(@properties.pos.x, @properties.pos.y, z) + normal_view[i] = vec2(utbl[@@loctbl[i][1]][1], utbl[@@loctbl[i][2]][2]) + +class BobberPhysicsComponent extends world.PhysicsComponent + new: (name, properties, shape, args) => + super(name, properties) + join: (entity) => + super(entity) + @gc = entity\get("graphics") + + +class Bobber extends ecs.Entity + new: (name, position) => + c = { + bobber: BobberGraphicsComponent("bobber",{pos: position}) + pc: world.PhysicsComponent("pc",{},"circle", {position.x, position.y, 0.1}) + } + super(name, c) + check: () => + --print("Bobber is looking for fish") + bp = @get("pc") + for other, _ in pairs(bp\collisions!) + --print("Found entity in collisions:", other) + if other.component and other.component.entity + --print("And it's component.entity is:", other.component.entity) + @which = other.component.entity + return true + return false + + +class BubblesGraphicsComponent extends world.GraphicsComponent + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + h = 1 + geom_view[1] = vec3(-h,-h,1) + geom_view[2] = vec3(-h,h,1) + geom_view[3] = vec3(-h,-h,1) + geom_view[4] = vec3(h,h,1) + geom_view[5] = vec3(h,-h,1) + geom_view[6] = vec3(-h,-h,1) + uv = sprites.player_normal + normal_view[1] = vec2(uv.s1,uv.t1) + normal_view[2] = vec2(uv.s1,uv.t2) + normal_view[3] = vec2(uv.s2,uv.t2) + normal_view[4] = vec2(uv.s2,uv.t2) + normal_view[5] = vec2(uv.s2,uv.t1) + normal_view[6] = vec2(uv.s1,uv.t1) + bind = @.node("bind") + +{:BobberGraphicsComponent, :BobberPhysicsComponent, :Bobber} diff --git a/src/color.moon b/src/color.moon index 447ae32..ebbdddd 100644 --- a/src/color.moon +++ b/src/color.moon @@ -1,11 +1,10 @@ color = + highlight: {191,228,230} + foreground: {186,161,196} + midground: {139,126,168} background: {100,113,136} shadow: {62,81,84} - midground: {139,126,168} - foreground: {186,161,196} - highlight: {191,228,230} - outline: {44,54,52} black: {34,40,37} am_color = {k, vec4(v[1]/255,v[2]/255,v[3]/255,1) for k,v in pairs(color)} @@ -20,7 +19,19 @@ am.ascii_color_map = { o: am_color.outline } +lake_color = + highlight: color.highlight + foreground: {113, 224, 214} + midground: {90, 172, 188} + background: {75, 120, 156} + shadow: {51, 78, 120} + black: color.black + +am_lake_color = {k, vec4(v[1]/255, v[2]/255, v[3]/255,1) for k,v in pairs(lake_color)} + { - color: color - am_color: am_color + :color + :am_color + :lake_color + :am_lake_color } diff --git a/src/controllers/fish.moon b/src/controllers/fish.moon new file mode 100644 index 0000000..1516574 --- /dev/null +++ b/src/controllers/fish.moon @@ -0,0 +1,54 @@ +ecs = require("ecs") +world = require("world") +controller = {} + +class FishControllerComponent extends ecs.Component + new: () => + print("Fish controller started") + @node = am.group! + join: (entity) => + super(entity) + graphic = entity\get("graphic") + assert(graphic, "Fish controller must have a graphic") + pred_component = entity\get("pred") + assert(pred_component, "Fish controller must have a predicted component") + net_component = entity\get("net") + assert(net_component, "Fish controller must have a net component") + @net = net_component + graphic.node\append(@node) + comp = @ + locpicker = coroutine.create(() => + comp\pick_next_location! + coroutine.yield! + ) + --either "swimming", "waiting", or "catching" + @state = "swimming" + @node\action(coroutine.create(() => + while comp.state == "swimming" + comp.state = "waiting" + start_wait = world.sync_time! + while world.sync_time! - start_wait < 4 + --TODO: look for nearby hooks and get caught + coroutine.yield! + if comp.state == "waiting" + comp\pick_next_location! + comp.state = "swimming" + while math.distance(pred_component.properties.pos, net_component.properties.next_loc) > 0.01 + --print("At ", pred_component.properties.pos, "waiting to get to next location, ", net_component.properties.next_loc, "it is ", math.distance(pred_component.properties.pos, net_component.properties.next_loc), " away") + coroutine.yield! + + )) + pick_next_location: () => + -- Pick somewhere to swim based on where we are? + -- This can only be done on the host. + if @net.properties.pos.x > 10 -- pick somewhere to the right + @net.properties.next_loc = vec2( + math.random(10.3,12), + math.random(-10,10) + ) + @net.properties.next_loc_time = world.sync_time! + --@net.properties.next_loc = @net.properties.pos + vec2(0,1) + --@net.properties.next_loc_time = world.sync_time! + print("Picking next location, it was ", @net.properties.next_loc) + +{:FishControllerComponent} diff --git a/src/controllers/mouse_keyboard.moon b/src/controllers/mouse_keyboard.moon index b0b63b6..28c4f58 100644 --- a/src/controllers/mouse_keyboard.moon +++ b/src/controllers/mouse_keyboard.moon @@ -1,6 +1,11 @@ ecs = require("ecs") win = require("window") world = require("world") +fish = require("spawn_fish") +rng = require("rng") +us = require("ui.sprites") +ui = require("ui") +assert(fish.Fish, "Failed to find fish from spawn_fish") controller = {} class MouseKeyboardControllerComponent extends ecs.Component @@ -10,21 +15,26 @@ class MouseKeyboardControllerComponent extends ecs.Component join: (ent) => graphic = ent\get("graphic") @node = am.group! - graphic.properties.node\append(@node) + graphic.node\append(@node) net_component = ent\get("net") + @net = net_component pred_component = ent\get("pred") - bind_node = graphic.properties.node("bind") + @pred = pred_component + bind_node = graphic.node("bind") + line = ent\get("line") + comp = @ + @minigame_started = false @node\action(() => x,y = 0,0 accel = {x:0,y:0} -- x,y if win\key_down("w") - y += 0.001 + y += 0.0006 if win\key_down("s") - y -= 0.001 + y -= 0.0006 if win\key_down("a") - x -= 0.001 + x -= 0.0006 if win\key_down("d") - x += 0.001 + x += 0.0006 mouse_loc = win\mouse_position! angle = -math.atan(mouse_loc.x / mouse_loc.y) if mouse_loc.y > 0 @@ -43,10 +53,122 @@ class MouseKeyboardControllerComponent extends ecs.Component pred_loc = pred_component.properties.pos world.world_x = pred_loc.x world.world_y = pred_loc.y + --graphic\move(pred_loc.x, pred_loc.y) bind_node.world_x = pred_loc.x bind_node.world_y = pred_loc.y - + if win\mouse_pressed("left") + if net_component.properties.casted and not pred_component.properties.can_reel and net_component.properties.reeling == 0 + --rectract line + net_component.properties.casted = false + elseif net_component.properties.casted and pred_component.properties.can_reel and net_component.properties.reeling == 0 + --catch a fish, gather all the info and delete the fish here. + print("Before starting, reeling is", net_component.properties.reeling) + net_component.properties.reeling = 1 + comp.bobber = ent.bobber + f = ent.bobber.which + f.state = "catching" + comp\start_minigame(f) + f\destroy! + elseif net_component.properties.reeling > 0 + print("Reeling in fish!") + else + worldpos = world.fromscreen(win\mouse_position!) + net_component.properties.cast = worldpos + net_component.properties.casted = true + net_component.properties.cast_time = world.sync_time! + print("Set cast", net_component.properties) + --test = require("test_entity") + --ent = test.TestEntity(nil, vec3(worldpos,-1.1)) + if win\mouse_pressed("right") + worldpos = world.fromscreen(win\mouse_position!) + print("fish is:", fish) + f = fish.Fish(nil,worldpos) + f\get("net").properties.next_loc = worldpos + vec2(0,5) + f\get("net").properties.next_loc_time = am.current_time! ) + start_minigame: (fish) => + @minigame_started = true + node = am.group! + hook_location = 0 -- 0 -> 1? + hook_width = 5 + hook_gravity = 5 -- how quickly do we fall + hook_force = 80 -- how quickly do we accelerate + hook_vel = 0 + hook_length = 1 + hook_bounce_damp = 0.2 --how elastic is the bottom and top? + fish_width = fish.width + fish_force = 100 + fish_gravity = 0 + fish_activity = 0.1 + fish_vel = 0 + fish_bounce_damp = 1 -- perfectly elsastic bounce + reel_progress = 0 + escape_progress = 0 + reel_speed = 0.1 + escape_speed = 0.01 + reel_cutoff = 10 + escape_cutoff = 10 + fish_location = math.random(-256,256) + bar_sprite = require("ui.button")(40,-256,64,512,"").node + node\append(bar_sprite) + hook_sprite = am.translate(0,0)\append(am.scale(1)\append(am.sprite(us.hook))) + node\append(hook_sprite) + fish_g_sprite = am.sprite(us.fish_purple) + fish_b_sprite = am.sprite(us.fish_blue) + fish_sprite = am.translate(0,0)\append(am.scale(1)\append(am.group(fish_g_sprite, fish_b_sprite))) + node\append(fish_sprite) + ui.node\append(node) + net = @net + bobber = @bobber + assert(bobber, "Failed to find bobber") + node\action(() => + if win\mouse_pressed("left") + hook_vel += hook_force + else + hook_vel -= hook_gravity + hook_location += hook_vel * am.delta_time + if hook_location < -256 + 16 -- bounce + hook_vel = -hook_vel * hook_bounce_damp + hook_location = -256 + 16 + elseif hook_location > 256 - 16 + hook_vel = -hook_vel * hook_bounce_damp + hook_location = 256 - 16 + if math.random! < fish_activity + fish_vel = fish_force * math.random(-1,1) + fish_location += fish_vel * am.delta_time + if fish_location < -256 + 16 -- bounce + fish_vel = -fish_vel * fish_bounce_damp + fish_location = -256 + 16 + elseif fish_location > 256 - 16 + fish_vel = -fish_vel * fish_bounce_damp + fish_location = 256 - 16 + + if hook_location - hook_width > fish_location - fish_width and hook_location + hook_width < fish_location + fish_width + fish_b_sprite.hidden = true + fish_g_sprite.hidden = false + reel_progress += reel_speed + else + escape_progress += escape_speed + fish_g_sprite.hidden = true + fish_b_sprite.hidden = false + if reel_progress > reel_cutoff + net.properties.fish_caught += 1 + net.properties.casted = false + net.properties.reeling = 0 + ui.node\remove(node) + bobber.which = nil + elseif escape_progress > escape_cutoff + net.properties.casted = false + net.properties.reeling = 0 + ui.node\remove(node) + bobber.which = nil + --print("reel:", reel_progress, "escape:", escape_progress) + -- Updates all sprites at the end? + hook_sprite("translate").y = hook_location + fish_sprite("translate").y = fish_location + + ) + --error("Starting fishing minigame") controller.text_size = 1 controller.Controller = MouseKeyboardControllerComponent diff --git a/src/ecs.moon b/src/ecs.moon index 182f09d..a865801 100644 --- a/src/ecs.moon +++ b/src/ecs.moon @@ -9,8 +9,12 @@ class Component @properties = properties or {} join: (e) => @ + post_join: (e) => + @ leave: (e) => @ + __tostring: () => + return string.format("%s<%s> {\n%s\n}",@@__name,@name or "no name given",tostring(@properties)) -- Base entity of our ECS class Entity @@ -34,10 +38,13 @@ class Entity @c_by_type[component.__class] = @c_by_type[component.__class] or {} @c_by_type[component.__class][name] = component for name, component in pairs(@components) + assert(component.join, "Component " .. name .. " does not have a join method.") component\join(@) + component\post_join(@) add: (cid, component) => component.entity = @ component\join(@) + component\post_join(@) if cid == nil cid = #@components while @components[cid] @@ -53,8 +60,14 @@ class Entity component @components[cid] = nil @c_by_type[component.__class][cid] = nil + destroy: () => + for name, component in pairs(@components) + @remove(name) + get: (cid) => @components[cid] + __tostring: () => + return string.format("%s {\n%s\n}",@@__name,tostring(@components)) class NetworkedComponent extends Component pack: () => @@ -69,6 +82,7 @@ class PredictedComponent extends Component new: (name, properties, netc_name, calculate) => super(name, properties) @netc_name = netc_name + assert(calculate and type(calculate) == "table", "Calculate must be a table") @calculate = calculate join: (entity) => @net = @entity\get(@netc_name) diff --git a/src/islandgen.moon b/src/islandgen.moon index 95e8b07..1fdd3eb 100644 --- a/src/islandgen.moon +++ b/src/islandgen.moon @@ -3,6 +3,10 @@ shim = require("shader_shim") world = require("world") color = require("color") win = require("window") +settings = require("settings") +sprites = require("world.sprites") +ecs = require("ecs") +fish = require("spawn_fish") gen = {} -- Generate roughly rectangular islands @@ -69,50 +73,111 @@ gen.generate = (seed) -> if i < 25 gview[gi+1] = 1 -gen.protogen = (seed) -> - geom = am.buffer(6 * 3 * 4) - gview = geom\view("vec3") - z = 0 - gview[1] = vec3(-10,-10,z) - gview[2] = vec3(-10,10,z) - gview[3] = vec3(10,10,z) - gview[4] = vec3(10,10,z) - gview[5] = vec3(10,-10,z) - gview[6] = vec3(-10,-10,z) - aspect = win.width / win.height - s_mv = mat4( - 1, 0, 0, 0, - 0, aspect, 0, 0, - 0, 0, 1, 0, - -0.5, 0.5, 0, 4 - ) - node = shim.land\append(am.depth_test("less")\append(am.bind({ - MV: s_mv - P: mat4(1) - land: gview - lamp1: vec4(0,0,0,0.1) - lamp2: vec4(0,0,0,0) - lamp3: vec4(0,0,0,0) - time: am.current_time! - background: color.am_color.background - shadow: color.am_color.shadow - midground: color.am_color.midground - foreground: color.am_color.foreground - highlight: color.am_color.highlight - outline: color.am_color.outline - black: color.am_color.black - world_x: 5 - world_y: 5 - })\append(am.draw("triangles")))) - node\action(() => - bind = self("bind") - bind.time = am.current_time! - bind.world_x = world.world_x - bind.world_y = world.world_y - print(world.world_x, world.world_y) - bind.lamp1 = vec4(math.sin(am.current_time!), 0, 0, math.cos(am.current_time! * 3) + math.pi) - ) +aspect = win.width / win.height +aspect_inv = win.height / win.width +class IslandGraphicsComponent extends world.GraphicsComponent + action: (bind) => + bind.time = am.current_time! % 1000 -- loop back around so noise doesn't look wonky after a while + --bind.lamp1 = vec4(world.world_x + 3.35, world.world_y + 1.9, 0, 2.2) --no idea why these magic numbers + --bind.lamp1 = vec4(world.world_x + (0.5 * 4 * aspect) - 0.3, world.world_y + (0.5 * 4 * (aspect_inv)) + 0.7, 0, 2.2) --no idea why these magic numbers + --bind.lamp2 = vec4(0,0,0,3) + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + z = 0 + size = 10 + --@lamp = world.level.add_lamp(vec4(0,0,0,3)) + geom_view[1] = vec3(-size,-size,z) + geom_view[2] = vec3(-size,size,z) + geom_view[3] = vec3(size,size,z) + geom_view[4] = vec3(size,size,z) + geom_view[5] = vec3(size,-size,z) + geom_view[6] = vec3(-size,-size,z) + normal_view[1] = vec2(sprites.rocks_normal.s1, sprites.rocks_normal.t1) + normal_view[2] = vec2(sprites.rocks_normal.s1, sprites.rocks_normal.t2) + normal_view[3] = vec2(sprites.rocks_normal.s2, sprites.rocks_normal.t2) + normal_view[4] = vec2(sprites.rocks_normal.s2, sprites.rocks_normal.t2) + normal_view[5] = vec2(sprites.rocks_normal.s2, sprites.rocks_normal.t1) + normal_view[6] = vec2(sprites.rocks_normal.s1, sprites.rocks_normal.t1) + +class WaterGraphicsComponent extends world.GraphicsComponent + action: (bind) => + bind.time = am.current_time! % 1000 + buf_size: () => + 24 + populate_buf: (geom_view, normal_view, offset) => + z = 1 + island_size = 10 - 1 + water_size = 20 + uv = sprites.sea_normal + --down + geom_view[1] = vec3(-water_size,-water_size,z) + geom_view[2] = vec3(-island_size,-island_size,z) + geom_view[3] = vec3(island_size,-island_size,z) + geom_view[4] = vec3(island_size,-island_size,z) + geom_view[5] = vec3(water_size,-water_size,z) + geom_view[6] = vec3(-water_size,-water_size,z) + --left + geom_view[7] = vec3(-water_size,-water_size,z) + geom_view[8] = vec3(-island_size,island_size,z) + geom_view[9] = vec3(-island_size,-island_size,z) + geom_view[10] = vec3(-water_size,-water_size,z) + geom_view[11] = vec3(-water_size,water_size,z) + geom_view[12] = vec3(-island_size,island_size,z) - node + --top + geom_view[13] = vec3(-water_size,water_size,z) + geom_view[14] = vec3(water_size,water_size,z) + geom_view[15] = vec3(-island_size,island_size,z) + geom_view[16] = vec3(-island_size,island_size,z) + geom_view[17] = vec3(water_size,water_size,z) + geom_view[18] = vec3(island_size,island_size,z) + + --right + geom_view[19] = vec3(island_size,island_size,z) + geom_view[20] = vec3(water_size,water_size,z) + geom_view[21] = vec3(island_size,-island_size,z) + geom_view[22] = vec3(water_size,water_size,z) + geom_view[23] = vec3(water_size,-water_size,z) + geom_view[24] = vec3(island_size,-island_size,z) + + normal_view[1] = vec2(uv.s1,uv.t1) + normal_view[2] = vec2(uv.s1,uv.t1) + normal_view[3] = vec2(uv.s2,uv.t1) + normal_view[4] = vec2(uv.s2,uv.t1) + normal_view[5] = vec2(uv.s2,uv.t1) + normal_view[6] = vec2(uv.s1,uv.t1) + normal_view[7] = vec2(uv.s1,uv.t1) + normal_view[8] = vec2(uv.s1,uv.t2) + normal_view[9] = vec2(uv.s1,uv.t1) + normal_view[10] = vec2(uv.s1,uv.t1) + normal_view[11] = vec2(uv.s1,uv.t2) + normal_view[12] = vec2(uv.s1,uv.t2) + normal_view[13] = vec2(uv.s1,uv.t2) + normal_view[14] = vec2(uv.s2,uv.t2) + normal_view[15] = vec2(uv.s1,uv.t2) + normal_view[16] = vec2(uv.s1,uv.t2) + normal_view[17] = vec2(uv.s2,uv.t2) + normal_view[18] = vec2(uv.s2,uv.t2) + normal_view[19] = vec2(uv.s2,uv.t2) + normal_view[20] = vec2(uv.s2,uv.t2) + normal_view[21] = vec2(uv.s2,uv.t1) + normal_view[22] = vec2(uv.s2,uv.t2) + normal_view[23] = vec2(uv.s2,uv.t1) + normal_view[24] = vec2(uv.s2,uv.t1) + setup: (node) => + bind = node("bind") + print("Found bind node:",node) + for k,v in pairs(color.am_lake_color) + bind[k] = v + bind.water = 2 + +gen.protogen = (seed) -> + ecs.Entity(nil, { + IslandGraphicsComponent("island") + }) + ecs.Entity(nil, { + WaterGraphicsComponent("water") + }) gen diff --git a/src/main.lua b/src/main.lua index d57afde..517e3fd 100644 --- a/src/main.lua +++ b/src/main.lua @@ -2,7 +2,9 @@ require("preload") local win = require("window") local pp = am.postprocess({ clear_color = vec4(0.2, 0.2, 0.3, 1), - depth_buffer = true + depth_buffer = true, + width = win.width, + height = win.height, }) local world = require("world") world.node = pp @@ -10,10 +12,10 @@ local stars = require("shaders.stars") pp:append(stars) --win.scene:append(stars) -local world_shader = require("shaders.world") +--local world_shader = require("shaders.world") --print("World shader:",world_shader) --win.scene:append(world_shader.node) -pp:append(world_shader.node) +--pp:append(world_shader.node) --local lake_shader = require("shaders.lake") --pp:append(lake_shader) @@ -41,5 +43,6 @@ require("log").observe(function(chunk) end print(table.concat({"[",chunk.level:upper(),"]",os.date()," > ",chunk.message})) end) +pp.clear_color = require("color").am_color.background --am.eval_js(require("js_bridge")) --local a,b = pcall(am.eval_js, require("js_bridge")) diff --git a/src/player.moon b/src/player.moon index fcd624b..da1c8dc 100644 --- a/src/player.moon +++ b/src/player.moon @@ -3,14 +3,12 @@ shim = require("shader_shim") win = require("window") sprites = require("world.sprites") world = require("world") +color = require("color") +bobber = require("bobber") +settings = require("settings") aspect = win.width / win.height +aspect_inv = win.height / win.width player_tiny = 6 -s_mv = mat4( - 1, 0, 0, 0, - 0, aspect, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, player_tiny - ) class Player extends ecs.Entity new: (router, controller, alias) => @router = router @@ -20,59 +18,141 @@ class Player extends ecs.Entity else error("Implement other players") +class PlayerLineGraphicComponent extends world.GraphicsComponent + @line_width = 1 + @static = false + @loctbl = { + {-1,-1} + {-1,1} + {1,1} + {1,1} + {1,-1} + {-1,-1} + } + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + @buf = geom_view + h = 0.5 + --@lamp_offset = vec4(2.5,1.4,0,0) + aspect = win.width / win.height + --@lamp = world.level.add_lamp(vec4(0,0,0,0.1 * aspect) + @lamp_offset) + uv = sprites.player_normal + utbl = { + [-1]: {uv.s1, uv.t1} + [1]: {uv.s2, uv.t2} + } + for i = 1,6 + loctbl = @@loctbl[i] + geom_view[i] = vec3(@@loctbl[i][1] * h, @@loctbl[i][2] * h, -1.5) + normal_view[i] = vec2(utbl[@@loctbl[i][1]][1], utbl[@@loctbl[i][2]][2]) + join: (entity) => + super(entity) + @net = entity\get("net") + assert(@net, "Failed to find network component on " .. tostring(entity)) + @pred = entity\get("pred") + @gc = entity\get("graphic") + [[ + @gc.node\append(@node) + ]] + s = @ + width = 0.01 + @node\action(() => + --to rotate correcly, we need to start at 00, and set the length + -- then rotate the whole thing + ce = s.pred.properties.cast_end + offset = s.net.properties.cast - s.pred.properties.pos + direction = math.atan(offset.x / offset.y) + math.pi + if offset.y > 0 + direction += math.pi + rot = math.rotate4(direction) + move2 = vec3(s.pred.properties.pos, 0) + scale = s.pred.properties.cast_end + z = -0.12 + local1 = vec4(-width, 0,z,1) * rot + local2 = vec4(-width, scale,z,1) * rot + local3 = vec4(width, scale,z,1) * rot + local4 = vec4(width, scale,z,1) * rot + local5 = vec4(width, 0,z,1) * rot + local6 = vec4(-width,0,z,1) * rot + s.buf[1] = local1.xyz + move2 + s.buf[2] = local2.xyz + move2 + s.buf[3] = local3.xyz + move2 + s.buf[4] = local4.xyz + move2 + s.buf[5] = local5.xyz + move2 + s.buf[6] = local6.xyz + move2 + if s.net.properties.casted and world.sync_time! - 1 > s.net.properties.cast_time and entity.bobber == nil + entity.bobber = bobber.Bobber(nil, s.net.properties.cast) + if world.level.on_land(s.net.properties.cast) + @action("bobbersfx", am.play(19726308, false, 1, settings.volume)) + else + @action("bobbersfx", am.play(82979201, false, 1, settings.volume)) + print("Should create bobber") + elseif not s.net.properties.casted and entity.bobber ~= nil + entity.bobber\destroy! + @action("bobberretract", am.play(57368801, false, 1, settings.volume)) + entity.bobber = nil + ) + forward: () => + --print("Forward called", @properties) + super! + @gc\move(@properties.pos.x, @properties.pos.y) + face: (direction) => + @.node("bind").rot = (direction ) % (math.pi * 2) + class PlayerGraphicComponent extends world.GraphicsComponent - @player_size = 1 - @static = true - new: (name, properties) => - properties = properties or {} - buf = am.buffer(6 * 3 * 4) - buf.usage = "dynamic" - @playerbuf = buf\view("vec3") - @playerbuf[1] = vec3(0,0,0) - @playerbuf[2] = vec3(0,@@player_size,0) - @playerbuf[3] = vec3(@@player_size,@@player_size,0) - @playerbuf[4] = vec3(@@player_size,@@player_size,0) - @playerbuf[5] = vec3(@@player_size,0,0) - @playerbuf[6] = vec3(0,0,0) - properties.node = shim.player\append(am.depth_test("less")\append(am.bind({ - MV: s_mv - P: mat4(1) - world_x: 0, - world_y: 0, - player: @playerbuf - texuv: am.vec2_array({ - vec2(0,0), - vec2(0,1), - vec2(1,1), - vec2(1,1), - vec2(1,0), - vec2(0,0) - }) - dir: 0 - textures: sprites.guy_diffuse.texture - })\append(am.draw("triangles")))) - properties.node\action(() => - --print("Player graphic action") + @player_size = 0.25 + @static = false + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + @playerbuf = geom_view + h = @@player_size / 2 + @lamp_offset = vec4(2.5,1.4,0,0) + @lamp = world.level.add_lamp(vec4(0,0,0,1 * aspect) + @lamp_offset) + geom_view[1] = vec3(-h,-h,1) + geom_view[2] = vec3(-h,h,1) + geom_view[3] = vec3(-h,-h,1) + geom_view[4] = vec3(h,h,1) + geom_view[5] = vec3(h,-h,1) + geom_view[6] = vec3(-h,-h,1) + uv = sprites.player_normal + normal_view[1] = vec2(uv.s1,uv.t1) + normal_view[2] = vec2(uv.s1,uv.t2) + normal_view[3] = vec2(uv.s2,uv.t2) + normal_view[4] = vec2(uv.s2,uv.t2) + normal_view[5] = vec2(uv.s2,uv.t1) + normal_view[6] = vec2(uv.s1,uv.t1) + join: (entity) => + super(entity) + aspect = win.width / win.height + -- inject nodes into the scene graph + program = @.node("use_program") + pred_component = entity\get("pred") + graphic = entity\get("graphic") + @node\remove(program) + @node\append(am.blend("alpha")\append(program)) + @node\action(() => + pred_loc = pred_component.properties.pos + graphic\move(pred_loc.x, pred_loc.y) ) - --print("Properties node is:", properties.node) - super(name, properties) - --print("finished init") - @ + --@.node("bind").highlight = color.am_color.black move: (x,y) => assert(x, "x required") assert(y, "y required") + world.level.move_lamp(@lamp, x + @lamp_offset.x, y + @lamp_offset.y) h = @@player_size / 2 - @playerbuf[1] = vec3(x-h,y-h,0) - @playerbuf[2] = vec3(x-h,y+h,0) - @playerbuf[3] = vec3(x+h,y+h,0) - @playerbuf[4] = vec3(x+h,y+h,0) - @playerbuf[5] = vec3(x+h,y-h,0) - @playerbuf[6] = vec3(x-h,y-h,0) + z = -1.5 + @playerbuf[1] = vec3(x-h,y-h,z) + @playerbuf[2] = vec3(x-h,y+h,z) + @playerbuf[3] = vec3(x+h,y+h,z) + @playerbuf[4] = vec3(x+h,y+h,z) + @playerbuf[5] = vec3(x+h,y-h,z) + @playerbuf[6] = vec3(x-h,y-h,z) --print("Move called", @playerbuf[1]) face: (direction) => - print("direction",direction) - @properties.node("bind").dir = (direction ) % (math.pi * 2) - --@properties.node("bind").MV = + --print("direction",direction) + @.node("bind").rot = (direction ) % (math.pi * 2) -- In a normal simulation, velocity adds acceleration * delta time every tick, minus some friction coefficient * current velocity @@ -97,49 +177,85 @@ class PlayerGraphicComponent extends world.GraphicsComponent -- we want to find the location based on inital velocity and position, constant acceleration, and delta time friction = 0.1 +cast_delay = 1 class PlayerPredictedComponent extends ecs.PredictedComponent new: (name) => - super(name, {vel: vec2(0,0), pos:vec2(0,0), accel: vec2(0,0)}, "net", { + super(name, {vel: vec2(0,0), pos:vec2(0,0), accel: vec2(0,0), cast_end: 0, can_reel:false}, "net", { accel:() => - vec2(@net.properties.accel) + vec2(@net.properties.accel) * (world.level.on_land(@properties.pos) and 1 or 0.5) vel: () => --print("Net is ", @net.properties) delta = world.sync_time! - @net.properties.last_update - (@net.properties.accel / friction) + ((@net.properties.vel - (@net.properties.accel / friction)) * math.exp(-friction * delta)) + (@properties.accel / friction) + ((@net.properties.vel - (@properties.accel / friction)) * math.exp(-friction * delta)) pos: () => delta = world.sync_time! - @net.properties.last_update - friction_loss = @net.properties.accel / friction + friction_loss = @properties.accel / friction -- when delta = 0 (up to date) -- pos = (1/friction) * (velocity - friction_loss) * 1 + position -- = 2 * (2 - 2) * 1 + position -- = position - (friction_loss * delta) - ((1/friction) * (@net.properties.vel - friction_loss) * (math.exp(-friction * delta))) + @properties.pos + (friction_loss * delta) - ((1/friction) * (@properties.vel - friction_loss) * (math.exp(-friction * delta))) + @properties.pos + cast_end: () => + if not @net.properties.casted + return 0 + since_cast = world.sync_time! - @net.properties.cast_time + dist = math.distance(@properties.pos, @net.properties.cast) + len = math.mix(0, dist, since_cast / cast_delay) + math.clamp(len,0,dist) + can_reel: () => + @ent.bobber and @ent.bobber\check! + }) print("Right after creation, properties is",@properties) @node = am.group! join: (entity) => + @ent = entity @gc = entity\get("graphic") @net = entity\get("net") - @gc.properties.node\append(@node) + @gc.node\append(@node) s = @ @node\action(() => s\forward! ) + @last_step = am.current_time! forward: () => + step_time = 0.4 + -- Step sound effect + land = world.level.on_land(@properties.pos) + if am.current_time! - @last_step > (step_time * (land and 1 or 0.75)) and math.length(@properties.vel) > 0 + print("Step playing") + if land + @node\action("stepsfx", am.play(60861008 + math.random(10) * 100, false, 1, settings.volume)) + else + @node\action("stepsfx", am.play(78618302 + math.random(8) * 10, false, 1, settings.volume)) + @last_step = am.current_time! + if @properties.cast_end > 0 and @properties.cast_end < 0.1 + @node\action("castsfx", am.play(41228309, false, 1, settings.volume)) --print("Forward called", @properties) super! @gc\move(@properties.pos.x, @properties.pos.y) - - class ProtoPlayer extends ecs.Entity new: () => @controller = require("controllers.mouse_keyboard") cc = require("controllers.mouse_keyboard").Controller() gc = PlayerGraphicComponent("graphic") pc = PlayerPredictedComponent("pred") - nc = ecs.NetworkedComponent("net",{accel: vec2(0,0), vel: vec2(0,0), pos: vec2(0,0), name: "test", last_update: 0, dir: 0}) + nc = ecs.NetworkedComponent("net",{ + accel: vec2(0,0), + vel: vec2(0,0), + pos: vec2(0,0), + name: "test", + last_update: 0, + dir: 0 + cast: vec2(0,0) + casted: false + cast_time: 0 + reeling: 0 + fish_caught: 0 + }) + lc = PlayerLineGraphicComponent("line") print("Protoplayer created") - super("test",{graphic:gc,net:nc,controller:cc,pred:pc}) + super("test",{graphic:gc,net:nc,controller:cc,pred:pc,line:lc}) {:Player, :ProtoPlayer} diff --git a/src/prefab/cabin.moon b/src/prefab/cabin.moon new file mode 100644 index 0000000..915ea92 --- /dev/null +++ b/src/prefab/cabin.moon @@ -0,0 +1,63 @@ +world = require("world") +sprites = require("world.sprites") +ecs = require("ecs") + +class CabinGraphicsComponent extends world.GraphicsComponent + -- + -- ##### + -- # # + -- ##/## + -- 3x3 floor, 1x3 * 3 walls, 1x1 * 2 door side walls, 1x1 door + buf_size: () => + (6*9) + (6*9) + (6* 2) + (6 * 1) + + populate_buf: (geom_view, normal_view, offset) => + z1 = -0.01 + z2 = -1 + iuv = sprites.wall_inside_normal + ouv = sprites.wall_outside_normal + fuv = sprites.floor_normal + wall = (geom, uv, offset, start, finish, texture) -> + geom[offset + 0] = vec3(start.x, start.y, z1) + geom[offset + 1] = vec3(start.x, start.y, z2) + geom[offset + 2] = vec3(finish.x, finish.y, z2) + geom[offset + 3] = vec3(finish.x, finish.y, z2) + geom[offset + 4] = vec3(finish.x, finish.y, z1) + geom[offset + 5] = vec3(start.x, start.y, z1) + uv[offset+0] = vec2(texture.s1,texture.t1) + uv[offset+1] = vec2(texture.s1,texture.t2) + uv[offset+2] = vec2(texture.s2,texture.t2) + uv[offset+3] = vec2(texture.s2,texture.t2) + uv[offset+4] = vec2(texture.s2,texture.t1) + uv[offset+5] = vec2(texture.s1,texture.t1) + + floor = (geom, uv, offset, start, finish) -> + geom[offset + 0] = vec3(start.x,start.y,z1) + geom[offset + 1] = vec3(start.x,finish.y,z1) + geom[offset + 2] = vec3(finish.x,finish.y,z1) + geom[offset + 3] = vec3(finish.x,finish.y,z1) + geom[offset + 4] = vec3(finish.x,start.y,z1) + geom[offset + 5] = vec3(start.x,start.y,z1) + normal_view[offset + 0] = vec2(fuv.s1, fuv.t1) + normal_view[offset + 1] = vec2(fuv.s1, fuv.t2) + normal_view[offset + 2] = vec2(fuv.s2, fuv.t2) + normal_view[offset + 3] = vec2(fuv.s2, fuv.t2) + normal_view[offset + 4] = vec2(fuv.s2, fuv.t1) + normal_view[offset + 5] = vec2(fuv.s1, fuv.t1) + + --left wall + j = 1 + wall(geom_view, normal_view, j, vec2(-2,-2),vec2(-2,0), sprites.wall_inside_normal) + j += 6 + for floorx = 1,3 + for floory = 1,3 + floor(geom_view, normal_view, j, vec2(-2 + (2*floorx), -2 + (2*floory)), vec2(2*floorx, 2*floory)) + j += 6 + + + +cabin = ecs.Entity("cabin",{ + graphic: CabinGraphicsComponent("graphic") +}) + +cabin diff --git a/src/preload.lua b/src/preload.lua index ca2dc9a..5b4034d 100644 --- a/src/preload.lua +++ b/src/preload.lua @@ -3,6 +3,7 @@ --[[ rewrite traceback function to map file names and line numbers to moonscript ]] +local require_order = {} local old_traceback = debug.traceback debug.traceback = function(...) local orig_traceback = old_traceback(...) @@ -23,9 +24,30 @@ debug.traceback = function(...) end end return string.format("%s:%d:", filename, linenum) - end) + end) .. "\nRequire order: [" .. table.concat(require_order, ",\n") .. "]" end +local old_require = require +local required = {} +require = function(...) + args = {...} + if not required[args[1]] then + required[args[1]] = true + table.insert(require_order, args[1]) + end + return old_require(...) +end + +--[[ +Display where print statements are comming from + +local oldprint = print +print = function(...) + oldprint(...) + oldprint(debug.traceback()) +end +]] + -- Override tostring to display more info about the table local old_tostring = tostring local numtabs = 0 diff --git a/src/router.moon b/src/router.moon index 2001952..94dbedd 100644 --- a/src/router.moon +++ b/src/router.moon @@ -88,6 +88,8 @@ class Router -- "uninitialized", "peer", "candidate", "elected" @state = "uninitialized" + -- Methods to call when our state changes + @onchange = {} -- [peerid] = "votes for peerid" @nonce = 0 @@ -202,6 +204,10 @@ class Router net.pump! return ret + onchange: (callback) => + table.insert(@onchange, callback) + #@onchange + broadcast: (msgfmt, message) => for peerid, conn in pairs(@peerlist) if peerid ~= @peer.id diff --git a/src/settings.moon b/src/settings.moon new file mode 100644 index 0000000..0cd1d76 --- /dev/null +++ b/src/settings.moon @@ -0,0 +1,11 @@ +-- like world, just holds state, but the per-client state instead of any shared state. + +settings = {} + +-- Passed to the land shader to add noise to lamps +settings.streamer = 0 + +-- Volume for sound effects +settings.volume = 1 + +settings diff --git a/src/shaders/lake.frag b/src/shaders/lake.frag index d45917e..c1c23f0 100644 --- a/src/shaders/lake.frag +++ b/src/shaders/lake.frag @@ -1,5 +1,93 @@ precision mediump float; -uniform vec4 color; +uniform vec4 black; +uniform vec4 outline; +uniform vec4 highlight; +uniform vec4 foreground; +uniform vec4 midground; +uniform vec4 shadow; +uniform vec4 background; +uniform vec4 lamp1; //vec3 position, float strength +uniform vec4 lamp2; +uniform vec4 lamp3; // max 3 lamps per shaded vertex +uniform vec4 lamp4; +uniform vec4 lamp5; +uniform vec4 lamp6; +uniform vec4 lamp7; +uniform vec4 lamp8; +uniform float streamer; // turn off the noise in the light +uniform float time; //used for noise +uniform sampler2D atlas; +uniform sampler2D previous; +uniform float nlamps; +varying vec2 worldxy; +varying vec2 norm; + +// Author @patriciogv - 2015 +float random (vec2 st) { + return fract( + sin( + dot(st.xy,vec2(12.9898,78.233)) + ) * + 43758.5453123 + ); +} + +// stolen from https://www.shadertoy.com/view/Msf3WH +float noise( in vec2 p ) +{ + const float K1 = 0.366025404; // (sqrt(3)-1)/2; + const float K2 = 0.211324865; // (3-sqrt(3))/6; + + vec2 i = floor( p + (p.x+p.y)*K1 ); + vec2 a = p - i + (i.x+i.y)*K2; + float m = step(a.y,a.x); + vec2 o = vec2(m,1.0-m); + vec2 b = a - o + K2; + vec2 c = a - 1.0 + 2.0*K2; + vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 ); + vec3 n = h*h*h*h*vec3( dot(a,random(i+0.0)), dot(b,random(i+o)), dot(c,random(i+1.0))); + return dot( n, vec3(70.0) ); +} + void main() { - gl_FragColor = color; + vec4 coord = gl_FragCoord + vec4(worldxy * 256., 0, 0); + /* + coord.x -= worldxy.x; + coord.y -= worldxy.y; + */ + //coord = coord / 1000.; + // calculate color at this pixel + vec4 normal = texture2D(atlas, norm); + float color = 0.; + vec4 lamp1_norm = lamp1 * 256.; + color += lamp1_norm.w - distance(lamp1_norm.xy - worldxy, coord.xy); + color = max(color,(lamp2.w * 256.) - distance((lamp2.xy * 256.) - worldxy, coord.xy)); + color = max(color,(lamp3.w * 256.) - distance((lamp3.xy * 256.) - worldxy, coord.xy)); + // divide to get a normalized color + //color /= (256. * max(max(lamp1.w, lamp2.w), lamp3.w)); + color /= 256. * 5.; + //color = sqrt(color / 2046.); + // Sett the normal texture under our lamplight + color = dot(vec4(color),normal) / 1.; + // make the colors fuzzy near the border (or don't if we're streaming) + float rng = random(vec2(coord.x, coord.y) + vec2(color, time)); + color -= (pow(rng / 1.3, 10. * color)) * streamer; + // add noise to the water + /* */ + if(color > 1.) + gl_FragColor = highlight * normal.a; + else if(color > 0.8) + gl_FragColor = foreground * normal.a; + else if(color > 0.6) + gl_FragColor = midground * normal.a; + else if(color > 0.4) + gl_FragColor = background * normal.a; + else if(color > 0.2) + gl_FragColor = shadow * normal.a; + else + gl_FragColor = black * normal.a; + /* + gl_FragColor = normal* vec4(color , color, color,1.); + */ + //gl_FragColor = normal* vec4(color , color / 10., color / 1024.,1.); } diff --git a/src/shaders/land.frag b/src/shaders/land.frag index 3c23990..1eb8d93 100644 --- a/src/shaders/land.frag +++ b/src/shaders/land.frag @@ -9,8 +9,18 @@ uniform vec4 background; uniform vec4 lamp1; //vec3 position, float strength uniform vec4 lamp2; uniform vec4 lamp3; // max 3 lamps per shaded vertex +uniform vec4 lamp4; +uniform vec4 lamp5; +uniform vec4 lamp6; +uniform vec4 lamp7; +uniform vec4 lamp8; +uniform float streamer; // turn off the noise in the light +uniform float time; //used for noise +uniform sampler2D atlas; +uniform float nlamps; +uniform float water; varying vec2 worldxy; -varying mat4 pre; +varying vec2 norm; // Author @patriciogv - 2015 float random (vec2 st) { @@ -22,6 +32,28 @@ float random (vec2 st) { ); } +// stolen from https://www.shadertoy.com/view/Msf3WH +vec2 hash( vec2 p ) // replace this by something better +{ + p = vec2( dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)) ); + return -1.0 + 2.0*fract(sin(p)*43758.5453123); +} +float noise( in vec2 p ) +{ + const float K1 = 0.366025404; // (sqrt(3)-1)/2; + const float K2 = 0.211324865; // (3-sqrt(3))/6; + + vec2 i = floor( p + (p.x+p.y)*K1 ); + vec2 a = p - i + (i.x+i.y)*K2; + float m = step(a.y,a.x); + vec2 o = vec2(m,1.0-m); + vec2 b = a - o + K2; + vec2 c = a - 1.0 + 2.0*K2; + vec3 h = max( 0.5-vec3(dot(a,a), dot(b,b), dot(c,c) ), 0.0 ); + vec3 n = h*h*h*h*vec3( dot(a,hash(i+0.0)), dot(b,hash(i+o)), dot(c,hash(i+1.0))); + return dot( n, vec3(70.0) ); +} + void main() { vec4 coord = gl_FragCoord + vec4(worldxy * 256., 0, 0); /* @@ -29,22 +61,40 @@ void main() { coord.y -= worldxy.y; */ //coord = coord / 1000.; + // calculate color at this pixel + vec4 normal = texture2D(atlas, norm); float color = 0.; - vec2 lamppos = (lamp1.xy * 256.) - (worldxy * 1.); - color += (lamp1.w * 256.) - distance((lamp1.xy * 256.) - worldxy, coord.xy); - /* + vec4 lamp1_norm = lamp1 * 256.; + color += lamp1_norm.w - distance(lamp1_norm.xy - worldxy, coord.xy); + color = max(color,(lamp2.w * 256.) - distance((lamp2.xy * 256.) - worldxy, coord.xy)); + color = max(color,(lamp3.w * 256.) - distance((lamp3.xy * 256.) - worldxy, coord.xy)); + // divide to get a normalized color + //color /= (256. * max(max(lamp1.w, lamp2.w), lamp3.w)); + color /= 256. * 5.; + //color = sqrt(color / 2046.); + // see the normal texture under the color + color = dot(vec4(color),normal) / 1.; + // make the colors fuzzy near the border (or don't if we're streaming) + float rng = random(vec2(coord.x, coord.y) + vec2(color, time)); + color -= (pow(rng / 1.3, 10. * color)) * streamer; + // add noise to water + if(water > 1.) + color += (noise(coord.xy + vec2(time, time)) - 0.0) * 0.1; + /* */ if(color > 1.) - gl_FragColor = highlight; + gl_FragColor = highlight * normal.a; else if(color > 0.8) - gl_FragColor = foreground; + gl_FragColor = foreground * normal.a; else if(color > 0.6) - gl_FragColor = midground; + gl_FragColor = midground * normal.a; else if(color > 0.4) - gl_FragColor = background; + gl_FragColor = background * normal.a; else if(color > 0.2) - gl_FragColor = shadow; + gl_FragColor = shadow * normal.a; else - gl_FragColor = black; + gl_FragColor = black * normal.a; + /* + gl_FragColor = normal* vec4(color , color, color,1.); */ - gl_FragColor = vec4(color / (256. * lamp1.w), color / (256. * lamp1.w), color / (256. * lamp1.w),1.); + //gl_FragColor = normal* vec4(color , color / 10., color / 1024.,1.); } diff --git a/src/shaders/land.vert b/src/shaders/land.vert index 2004ab3..1c9b9f3 100644 --- a/src/shaders/land.vert +++ b/src/shaders/land.vert @@ -1,14 +1,26 @@ precision highp float; attribute vec3 land; -uniform float time; //used for noise +attribute vec2 landnormal; +uniform float rot; uniform float world_x; uniform float world_y; uniform mat4 MV; uniform mat4 P; varying vec2 worldxy; varying mat4 pre; +varying vec2 norm; void main() { + norm = landnormal; + mat2 rotate = mat2( + cos(rot), -sin(rot), + sin(rot), cos(rot) + ); worldxy = vec2(world_x, world_y); pre = P * MV; - gl_Position = P * MV * vec4(land.x - world_x, land.y - world_y, 0., 1.0); + vec2 local = (land.xy - worldxy) * rotate; + float z_scale = 0.5; + // clamp so that everything becomes orthographic once we move away + float xoff = clamp(land.z * local.x * z_scale, -0.5, 0.5); + float yoff = clamp(land.z * local.y * z_scale, -0.5, 0.5); + gl_Position = P * MV * vec4(local.xy - vec2(xoff, yoff), land.z, 1.0); } diff --git a/src/shaders/stars.frag b/src/shaders/stars.frag new file mode 100644 index 0000000..d45917e --- /dev/null +++ b/src/shaders/stars.frag @@ -0,0 +1,5 @@ +precision mediump float; +uniform vec4 color; +void main() { + gl_FragColor = color; +} diff --git a/src/shaders/stars.lua b/src/shaders/stars.lua index 8d54b16..3e4138e 100644 --- a/src/shaders/stars.lua +++ b/src/shaders/stars.lua @@ -1,57 +1,43 @@ local win = require("window") local color = require("color") local world = require("world") -local numstars = 400 -- we might have as many as 4 over +local shim = require("shader_shim") +local numstars = 500 -- we might have as many as 4 over local genned_stars = 0 local period_x = 3 local period_y = 3 local stars = {} -while genned_stars < numstars do +local tries = 0 +aspect = win.width / win.height +while genned_stars < numstars and tries < 100000 do local rngx = math.random() local xpos = rngx * win.width --* (period_x - 1) local rngy = math.random() local ypos = rngy * win.height --* (period_y - 1) local blinks = math.random() > 0.3 and (math.random() * 2 * math.pi) or 0 - stars[#stars+1] = vec3(xpos, ypos, blinks) - genned_stars = genned_stars + 1 - if xpos < win.width then - -- duplicate on the last screen - stars[#stars+1] = vec3(xpos + (win.width * (period_x-2)), ypos, blinks) - genned_stars = genned_stars + 1 - end - if ypos < win.height then - stars[#stars+1] = vec3(xpos, ypos + (win.height * (period_y-2)), blinks) - genned_stars = genned_stars + 1 - end - if xpos < win.width and ypos < win.height then - stars[#stars+1] = vec3(xpos + (win.width * (period_x-2)), ypos+(win.height * (period_y-2)),blinks) + --if math.distance(vec2(rngx,rngy), vec2(0.53,0.5)) > 0.5 then + local off = vec2(math.abs(rngx - 0.50) * aspect, math.abs(rngy-0.5)) + if math.length(off) > 0.5 then + stars[#stars+1] = vec3(xpos, ypos, blinks) genned_stars = genned_stars + 1 + if xpos < win.width then + -- duplicate on the last screen + stars[#stars+1] = vec3(xpos + (win.width * (period_x-2)), ypos, blinks) + genned_stars = genned_stars + 1 + end + if ypos < win.height then + stars[#stars+1] = vec3(xpos, ypos + (win.height * (period_y-2)), blinks) + genned_stars = genned_stars + 1 + end + if xpos < win.width and ypos < win.height then + stars[#stars+1] = vec3(xpos + (win.width * (period_x-2)), ypos+(win.height * (period_y-2)),blinks) + genned_stars = genned_stars + 1 + end end + tries = tries + 1 end -local node = am.use_program(am.program([[ - precision highp float; - attribute vec3 stars; - uniform float time; - uniform float world_x; - uniform float world_y; - uniform float world_x_period; - uniform float world_y_period; - uniform mat4 MV; - uniform mat4 P; - void main() { - float world_x_off = mod(world_x, world_x_period); - float world_y_off = mod(world_y, world_y_period); - gl_Position = P * MV * vec4(stars.x - world_x_off, stars.y - world_y_off, 0., 1.0); - float intensity = sin(stars.z + time) * cos(time) + 1.; - gl_PointSize = pow(intensity, 2.) * stars.z * 0.3; - } - ]],[[ - precision mediump float; - uniform vec4 color; - void main() { - gl_FragColor = color; - } -]])) +assert(genned_stars == numstars, "Failed to generate stars") +local node = am.blend("premult") ^ shim.stars ^ am.bind({ MV = mat4( 1, 0, 0, 0, @@ -66,11 +52,24 @@ local node = am.use_program(am.program([[ world_y = am.current_time(), world_y_period = (period_y - 2) * win.height, time = am.current_time(), + lamp1 = vec3(0), + lamp2 = vec3(0), + lamp3 = vec3(0), + lamp4 = vec3(0), + lamp5 = vec3(0), + lamp6 = vec3(0), + lamp7 = vec3(0), + lamp8 = vec3(0) }) ^ am.draw("points") node:action(function(self) self("bind").time = am.current_time() self("bind").world_x = world.world_x self("bind").world_y = world.world_y + local lamps = world.level.lamps_on_screen() + for i,v in pairs(lamps) do + print("Setting lamp", i, "to", v) + self("bind")["lamp" .. tostring(i)] = v + end end) return node diff --git a/src/shaders/stars.vert b/src/shaders/stars.vert new file mode 100644 index 0000000..8737482 --- /dev/null +++ b/src/shaders/stars.vert @@ -0,0 +1,30 @@ +precision highp float; +attribute vec3 stars; +uniform float time; +uniform float world_x; +uniform float world_y; +uniform float world_x_period; +uniform float world_y_period; +uniform vec4 lamp1; +uniform vec4 lamp2; +uniform vec4 lamp3; +uniform vec4 lamp4; +uniform vec4 lamp5; +uniform vec4 lamp6; +uniform vec4 lamp7; +uniform vec4 lamp8; +uniform mat4 MV; +uniform mat4 P; +void main() { + float world_x_off = mod(world_x, world_x_period); + float world_y_off = mod(world_y, world_y_period); + vec4 pos = P * MV * vec4(stars.x - world_x_off, stars.y - world_y_off, -0.1, 1.0); + gl_Position = pos; + float intensity = sin(stars.z + time) * cos(time) + 1.; + /* + if(distance(pos.xyz, lamp1.xyz) < 80.) + intensity = 0.; + */ + gl_PointSize = pow(intensity, 2.) * stars.z * 0.3; + //gl_PointSize = distance(pos.xyz, lamp1.xyz); +} diff --git a/src/spawn_fish.moon b/src/spawn_fish.moon new file mode 100644 index 0000000..5bc090b --- /dev/null +++ b/src/spawn_fish.moon @@ -0,0 +1,130 @@ +router = require("router") +ecs = require("ecs") +world = require("world") +win = require("window") +sprites = require("world.sprites") + +class FishGraphicComponent extends world.GraphicsComponent + @fish_size = 0.5 + @static = false + @loctbl = { + {-1,-1} + {-1,1} + {1,1} + {1,1} + {1,-1} + {-1,-1} + } + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + @buf = geom_view + h = @@fish_size / 2 + --@lamp_offset = vec4(2.5,1.4,0,0) + aspect = win.width / win.height + --@lamp = world.level.add_lamp(vec4(0,0,0,0.1 * aspect) + @lamp_offset) + for i = 1,6 + loctbl = @@loctbl[i] + geom_view[i] = vec3(@@loctbl[i][1] * h, @@loctbl[i][2] * h, -1.5) + uv = sprites.player_normal + normal_view[1] = vec2(uv.s1,uv.t1) + normal_view[2] = vec2(uv.s1,uv.t2) + normal_view[3] = vec2(uv.s2,uv.t2) + normal_view[4] = vec2(uv.s2,uv.t2) + normal_view[5] = vec2(uv.s2,uv.t1) + normal_view[6] = vec2(uv.s1,uv.t1) + join: (entity) => + super(entity) + aspect = win.width / win.height + program = @.node("use_program") + @pred = entity\get("pred") + graphic = entity\get("graphic") + assert(@pred, "Fish graphic must have a predicted component") + @node\remove(program) + @node\append(am.blend("alpha")\append(program)) + @ent = entity + comp = @ + move: (pos) => + h = @@fish_size / 2 + --print("Calling move with", pos) + --if @ent.state ~= "swimming" + -- error("called move while not swimming") + for i = 1,6 + @buf[i] = vec3(@@loctbl[i][1] * h, @@loctbl[i][2] * h, -0.13) + vec3(pos.x, pos.y, 0) + +friction = 0.1 +class FishPredictedComponent extends ecs.PredictedComponent + new: (id) => + fish_speed = 0.00001 + super(id, {accel: vec2(0,0), vel: vec2(0,0), pos: vec2(0,0)}, "netc", { + accel: () => + --print("ent state:", @cc.state) + if @cc.state ~= "swimming" + return vec2(0,0) + localized = @net.properties.next_loc - @properties.pos + --direction = math.atan(localized.y / localized.x) + math.normalize(localized + @properties.vel) * fish_speed + vel: () => + --print("Before calculating vel, properties was ", @properties) + delta = world.sync_time! - @net.properties.next_loc_time + --print("Delta time:", delta) + (@properties.accel / friction) + ((@properties.vel - (@properties.accel / friction)) * math.exp(-friction * delta)) + pos: () => + if @properties.pos.x > 20 + error("Very far, stopping") + delta = world.sync_time! - @net.properties.next_loc_time + friction_loss = @properties.accel / friction + (friction_loss * delta) - ((1/friction) * (@properties.vel - friction_loss) * (math.exp(-friction * delta))) + @properties.pos + }) + @node = am.group! + join: (entity) => + @net = entity\get("net") + @gc = entity\get("graphic") + @properties.pos = @net.properties.pos + @cc = entity\get("control") + @ent = entity + print("Got graphic component:",@gc, @gc.node) + print("And my node is", @node) + @gc.node\append(@node) + s = @ + @node\action(() => + s\forward! + ) + forward: () => + super! + if @ent.state == "catching" + @gc.node.hidden=true + else + @gc\move(@properties.pos) + +class Fish extends ecs.Entity + new: (id, pos) => + @width = 40 -- TODO: randomize fish width + components = { + pred: FishPredictedComponent("pred") + net: ecs.NetworkedComponent("net",{pos: pos, next_loc: pos, next_loc_time: am.current_time!}) + graphic: FishGraphicComponent("graphic") + control: require("controllers.fish").FishControllerComponent() + phys: world.PhysicsComponent("phys",{},"circle",{pos.x, pos.y, @width / 256}) + } + super(id, components) + destroy: (...) => + super(...) + error("Where did fish go") + +class SpawnFishComponent extends ecs.Component + new: (id, properties, spawnrect) => + super(id, properties) + + join: (entity) => + graphic = entity\get("graphic") + router = router.r! + set_spawnable = () -> + if router.state == "elected" + graphic\action(() -> + error("Spawn fish callback") + ) + set_spawnable! + router\onchange(set_spawnable) + +{:Fish, :SpawnFishComponent} diff --git a/src/test_entity.moon b/src/test_entity.moon new file mode 100644 index 0000000..a9ae170 --- /dev/null +++ b/src/test_entity.moon @@ -0,0 +1,36 @@ + +ecs = require("ecs") +world = require("world") +sprites = require("world.sprites") + +class TestGraphicComponent extends world.GraphicsComponent + @test_size = 0.5 + @static = false + buf_size: () => + 6 + populate_buf: (geom_view, normal_view, offset) => + assert(@properties.pos, "Test entity needs a position") + @buf = geom_view + h = @@test_size / 2 + pos = @properties.pos + geom_view[1] = vec3(-h,-h,1) + pos + geom_view[2] = vec3(-h,h,1) + pos + geom_view[3] = vec3(-h,-h,1) + pos + geom_view[4] = vec3(h,h,1) + pos + geom_view[5] = vec3(h,-h,1) + pos + geom_view[6] = vec3(-h,-h,1) + pos + uv = sprites.player_normal + normal_view[1] = vec2(uv.s1,uv.t1) + normal_view[2] = vec2(uv.s1,uv.t2) + normal_view[3] = vec2(uv.s2,uv.t2) + normal_view[4] = vec2(uv.s2,uv.t2) + normal_view[5] = vec2(uv.s2,uv.t1) + normal_view[6] = vec2(uv.s1,uv.t1) + +class TestEntity extends ecs.Entity + new: (id, pos) => + super(id,{ + graphic: TestGraphicComponent("graphic",{pos: pos}) + }) + +{:TestEntity} diff --git a/src/window.moon b/src/window.moon index 8aa8f2a..367cd1c 100644 --- a/src/window.moon +++ b/src/window.moon @@ -1,10 +1,10 @@ - +color = require("color") -- Special file to hold the window, no dependencies! win = am.window{ title: "ggj25" width: 1280 height: 720 - clear_color: vec4(0.2, 0.2, 0.3, 1) + clear_color: color.am_color.background --depth_buffer: true } win.scene = am.group! diff --git a/src/world.moon b/src/world.moon index 6a0eed8..60cb57b 100644 --- a/src/world.moon +++ b/src/world.moon @@ -2,8 +2,12 @@ win = require("window") hc = require("party.hc.init") ecs = require("ecs") +settings = require("settings") +color = require("color") +shim = require("shader_shim") +log = require("log") - +MAX_LAMPS = 8 print("hc:", hc) --Use a collider to decide what to render x = { @@ -20,22 +24,155 @@ x = { graphics:{} entities:{} graphic_world: hc.new(5) + physics_world: hc.new(1) + lamps: {} + lamps_rev: {} } sync_time: () -> am.current_time! } -x.level.collider = x.level.graphic_world\rectangle(0,0,1,1/win.width) +x.level.collider = x.level.graphic_world\rectangle(-1,-1,2,2) +x.level.add_lamp = (lamp) -> --vector4, vec3 position, strength + shape = x.level.graphic_world\circle(lamp.x, lamp.y, lamp.w * 2) + x.level.lamps[#x.level.lamps+1] = {lamp, shape} + x.level.lamps_rev[shape] = #x.level.lamps + #x.level.lamps +x.level.move_lamp = (id,newx, newy) -> + oldlamp = x.level.lamps[id][1] + x.level.lamps[id][2]\moveTo(newx, newy) + x.level.lamps[id][1] = vec4(newx, newy, oldlamp.z, oldlamp.w) +x.level.remove_lap = (id) -> + lamp = table.remove(x.level.lamps,id) + x.level.lamps_rev[lamp[2]] = nil + x.level.graphic_world\remove(lamp[2]) +x.level.lamps_on_screen = () -> + lamps = {} + i = 0 + -- collider world is not alligned to the graphic world + x.level.collider\moveTo(x.world_x + 3, x.world_y + 3) + for shape,_ in pairs(x.level.graphic_world\collisions(x.level.collider)) + index = x.level.lamps_rev[shape] + lamp = x.level.lamps[index] + table.insert(lamps, lamp[1]) + i += i + if i > MAX_LAMPS + return lamps + lamps +x.fromscreen = (pos) -> + print("fromscreen pos is", pos) + -- convert creen pixel coords to world xy coords + localx = (pos.x * (win.width / win.height) * 4) / (win.width + 1) + localy = (pos.y * 1 * 4) / win.height + globalx = x.world_x + localx + globaly = x.world_y + localy + vec2(globalx, globaly) +x.level.on_land = (pos) -> + not (pos.x > 10 or pos.x < -10 or pos.y > 10 or pos.y < -10) + +class PhysicsComponent extends ecs.Component + new: (name, properties, shape, args) => + log.info("Created a physics component " .. shape .. " with " .. tostring(args), {"phys"}) + assert(args, "Physcs Component must be passed args") + assert(type(shape) == "string" , "Shape must be a string, was " .. tostring(shape)) + @node = am.group! + super(name, properties) + hc = x.level.physics_world + table.insert(args,1,hc) + print("Creating a physics component with", args) + @shape = hc[shape](unpack(args)) + @shape.component = @ + join: (entity) => + super(entity) + @ent = entity + leave: () => + super! + x.level.physics_world\remove(@shape) + collisions: () => + x.level.physics_world\collisions(@shape) + class GraphicsComponent extends ecs.Component new: (name, properties) => print("Got name", name, "and properties", properties) - assert(properties and properties.node , "Failed to find node for graphics component") + @node = am.group! + --assert(properties and properties.node , "Failed to find node for graphics component") super(name, properties) - x.node\append(properties.node) + --x.node\append(properties.node) + join: (entity) => + buf_size = @buf_size! + print("Creating entity " .. entity.id .. " with graphic component with " .. buf_size .. " vertexes") + @geom_buffer = am.buffer(buf_size * 3 * 4)\view("vec3") -- 3 floats per vertex, 4 bytes per float + @norm_buffer = am.buffer(buf_size * 2 * 4)\view("vec2") -- 2 floats per uv, 4 bytes per float + @populate_buf(@geom_buffer, @norm_buffer) + aspect = win.width / win.height + s_mv = mat4( + 1, 0, 0, 0, + 0, aspect, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 4 + ) + binds = { + MV: s_mv + P: mat4(1) + land: @geom_buffer + landnormal: @norm_buffer + time: am.current_time! + atlas: require("world.sprites").rocks_normal.texture + streamer: 1 + world_x: x.world_x + world_y: x.world_y + rot: 0 + water: 0 + } + for color, value in pairs(color.am_color) + binds[color] = value + for i = 1,8 do + binds["lamp" .. tostring(i)] = vec4(0) + @node\append( + shim.land\append( + am.depth_test("less")\append( + am.cull_face("front")\append( + am.bind(binds)\append( + am.draw("triangles") + ))))) + print("graphic node created for",@,@node) + component = @ + @node\action(() => + bind = component.node("bind") + if component.action + component\action(bind) + bind.world_x = x.world_x + bind.world_y = x.world_y + --find the lamps in screen to render + lamps = x.level.lamps_on_screen! + --print("Found lamps:", lamps, "at",x.world_x, x.world_y) + for i = 1,8 do + if i <= #lamps + --print("Setting lamp",i,"to",lamps[i]) + bind["lamp" .. tostring(i)] = lamps[i] + else + bind["lamp" .. tostring(i)] = vec4(0) + --bind.lamps = am.vec4_array(lamps) + --bind.nlamps = #lamps + --print(world.world_x, world.world_y) + --bind.lamp1 = vec4(math.sin(am.current_time!), 0, 0, math.cos(am.current_time! * 3) + math.pi) + -- magic numbers to center the player :/ + bind.time = am.current_time! % 1000 -- loop back around so noise doesn't look wonky after a while + bind.streamer = settings.streamer + ) + if @setup + @setup(@node) + print("Appending ", @node, " to the scene") + x.node\append(@node) + leave: () => + x.node\remove(@node) static: () => @@static - node: () => - @properties.node + buf_size: () => + error("Subclasses of GraphicsComponent must implement buffer size") + populate_buf: (geom_view, normal_view, offset) => + error("Subclasses of GraphicsComponent must implement buffer populate method") x.GraphicsComponent = GraphicsComponent +x.PhysicsComponent = PhysicsComponent x diff --git a/src/worldgen.moon b/src/worldgen.moon index 5fcefaf..e4818d4 100644 --- a/src/worldgen.moon +++ b/src/worldgen.moon @@ -9,9 +9,13 @@ island = require("islandgen") --Check for setup --char = player.Player(world.router, world.controller ,text.text.text) --level = island.generate(1234) -level = island.protogen(1234) -world.node\append(level) +island_ent = island.protogen(1234) +cabin = require("prefab.cabin") +--world.node\append(level) --world.controller = require("controllers.mouse_keyboard") --world.node\append(world.controller.node) char = player.ProtoPlayer() gc = char\get("graphic") +nc = char\get("net") +nc.properties.accel = vec2(0.1,0) +nc.properties.last_update = am.current_time! - 0.1 -- cgit v1.2.3-70-g09d2