router = require("router") ecs = require("ecs") world = require("world") win = require("window") sprites = require("world.sprites") colors = require("color") class BubbleGraphicsComponent extends world.GraphicsComponent @loctbl = { {-1,-1} {-1,1} {1,1} {1,1} {1,-1} {-1,-1} } @max_bubbles = 6--128 @bubble_anim = { sprites.sphere_normal_1 sprites.sphere_normal_2 sprites.sphere_normal_3 sprites.sphere_normal_4 sprites.sphere_normal_5 sprites.sphere_normal_6 } buf_size: () => @@max_bubbles * 6 populate_buf: (geom_buffer, uv_buffer, offset) => -- we only need to store 1 frame info per 6 vertexes @bubble_frames = am.buffer(@buf_size! / 6)\view("ubyte") @buf = geom_buffer @uv = uv_buffer for i = 1, @@max_bubbles * 6-- must be divisible by 6 geom_buffer[i] = vec3(0,0,0) uv_buffer[i] = vec2(1,0) join: (entity) => super(entity) aspect = win.width / win.height program = @.node("use_program") @cursor = 0 @node\remove(program) @node\append(am.blend("off")\append(program)) bind = @.node("bind") bind.water = 3 for name, color in pairs(colors.am_lake_color) bind[name] = color @ent = entity comp = @ max_frames = @buf_size! / 6 bf = @bubble_frames uv = @uv buf = @buf @node\action(() -> bubble_anim = comp.__class.bubble_anim for i = 1,max_frames last_frame = bf[i] if last_frame < 6 next_frame = last_frame + 1 nuv = bubble_anim[next_frame] ustart = (i-1) * 6 uv[ustart + 1] = vec2(nuv.s1, nuv.t1) uv[ustart + 2] = vec2(nuv.s2, nuv.t1) uv[ustart + 3] = vec2(nuv.s2, nuv.t2) uv[ustart + 4] = vec2(nuv.s2, nuv.t2) uv[ustart + 5] = vec2(nuv.s1, nuv.t2) uv[ustart + 6] = vec2(nuv.s1, nuv.t1) bf[i] += 1 ) add_bubble: (pos) => h = 0.1 --print("About to add bubble, cursor is", @cursor) @bubble_frames[(@cursor / 6)+1] = 1 for i = 1,6 @buf[@cursor + i] = vec3(@@loctbl[i][1]*h + pos.x, @@loctbl[i][2]*h + pos.y, 0) @cursor += 6 if @cursor == (@@max_bubbles * 6) @cursor = 0 [[ bg = BubbleGraphicsComponent("graphic",{}) bubble_ent = ecs.Entity(nil,{ graphic: bg }) bubble = (pos) -> -- Create a bubble bg\add_bubble(pos) ]] class FishGraphicComponent extends world.GraphicsComponent @fish_size = 0.3 @static = false @loctbl = { {-1,-1} {-1,1} {1,1} {1,1} {1,-1} {-1,-1} } @bubble_freq = 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.fish_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)) bind = @.node("bind") for name, color in pairs(colors.am_lake_color) bind[name] = color bind.water = 3 @ent = entity @last_bubble = am.current_time! comp = @ move: (pos) => h = @@fish_size / 2 --print("Calling move with", pos) --if @ent.state ~= "swimming" -- error("called move while not swimming") --@phys.shape\moveTo(pos.x,pos.y) for i = 1,6 @buf[i] = vec3(@@loctbl[i][1] * h, @@loctbl[i][2] * h, -0.13) + vec3(pos.x, pos.y, 0) --Fuck the bubles, the fish looks better without em --if am.current_time! - @last_bubble > @@bubble_freq --world.Bubble(pos) --bubble(pos) --print("should spawn bubble") --@last_bubble = am.current_time! friction = 0.1 class FishPredictedComponent extends ecs.PredictedComponent new: (id) => fish_speed = 0.0001 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") @pc = entity\get("phys") @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 --TODO: Fish debugging --@gc.node.hidden=true @gc\move(@properties.pos) @pc.shape\moveTo(@properties.pos.x, @properties.pos.y) max_fish = 100 nfish = 0 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) nfish += 1 destroy: (...) => super(...) class SpawnFishComponent extends ecs.Component new: (id, properties, spawnrect) => super(id, properties) @spawnrect = spawnrect @spawned = 0 @last_spawned = world.sync_time! @spawn_freq = 8 join: (entity) => --TODO: only do this on the elected peer graphic = entity\get("water") assert(graphic, "Spawn fish must have a graphic node") comp = @ graphic.node\action(() => if nfish < max_fish and world.sync_time! - comp.last_spawned > comp.spawn_freq comp.last_spawned = world.sync_time! comp\spawn_fish! ) for i = 1,10 do @spawn_fish! spawn_fish: () => rngx = math.random(@spawnrect.x, @spawnrect.z) rngy = math.random(@spawnrect.y, @spawnrect.w) Fish(nil, vec2(rngx, rngy)) [[ router = router.r! set_spawnable = () -> if router.state == "elected" graphic\action(() -> error("Spawn fish callback") ) ]] --set_spawnable! --router\onchange(set_spawnable) [[ graphic\action(() -> error("spawn fish component") ) ]] {:Fish, :SpawnFishComponent}