diff --git a/src/hexyz.lua b/src/hexyz.lua index f29dc55..20c2eda 100644 --- a/src/hexyz.lua +++ b/src/hexyz.lua @@ -6,11 +6,6 @@ local function round(n) return n % 1 >= 0.5 and math.ceil(n) or math.floor(n) en ----- HEX CONSTANTS AND UTILITY FUNCTIONS ----- ============================================================================]]-- --- Hex Equality - Meant to Operate on two Amulet Vectors (vec2) -function hex_equals(a, b) return a[1] == b[1] and a[2] == b[2] end -function hex_not_equals(a, b) return not hex_equals(a, b) end - - -- All Non-Diagonal Vector Directions from a Given Hex by Edge HEX_DIRECTIONS = {vec2( 1 , -1), vec2( 1 , 0), vec2(0 , 1), vec2(-1 , 1), vec2(-1 , 0), vec2(0 , -1)} @@ -113,7 +108,7 @@ end -- Offset Coordinates Look Nice / are Useful for UI-Implementations function hex_to_offset(hex) - return vec2(hex[1], -hex[1]-hex[2] + (hex[1] + (hex[1] % 2)) / 2) end + return vec2(hex[1], -hex[1] - hex[2] + (hex[1] + (hex[1] % 2)) / 2) end -- ... Back to Cube Coordinates @@ -157,7 +152,7 @@ end -- Used to Retrieve Noise Values in Hashmap; t[vec2(x, y)] will always find nil function hash_retrieve(map, hex) for h,n in pairs(map) do - if hex_equals(hex, h) then + if hex == h then return n end end @@ -224,7 +219,6 @@ function hexagonal_map(radius, seed) local seed = seed or math.random(radius * 2 * math.pi) local map = {} - local mt = {__index={radius=radius, seed=seed}} for i = -radius, radius do local j1 = math.max(-radius, -i - radius) local j2 = math.min(radius, -i + radius) @@ -245,7 +239,7 @@ function hexagonal_map(radius, seed) map[vec2(i, j)] = noise end end - setmetatable(map, mt) + setmetatable(map, {__index={radius=radius, seed=seed}}) return map end @@ -255,8 +249,6 @@ function rectangular_map(width, height, seed) local seed = seed or math.random(width * height) local map = {} - local mt = {__index={width=width, height=height, seed=seed}} - for i = 0, width do for j = 0, height do @@ -274,7 +266,7 @@ function rectangular_map(width, height, seed) map[vec2(i, j - math.floor(i/2))] = noise end end - setmetatable(map, mt) + setmetatable(map, {__index={width=width, height=height, seed=seed}}) return map end diff --git a/src/main.lua b/src/main.lua index 906b215..68b0cc1 100644 --- a/src/main.lua +++ b/src/main.lua @@ -1,29 +1,60 @@ require"hexyz" -math.randomseed(os.time()) +math.randomseed(os.time()); math.random(); math.random(); math.random() -local win = am.window +win = am.window { -- Base Resolution = 3/4 * WXGA standard 16:10 width = 1280 * 3/4, -- 960px height = 800 * 3/4, -- 600px + resizable = false, + clear_color = vec4(0.08, 0.08, 0.11, 1) } +local bias = "right" + local map local world local home local home_node -function find_home() - home = spiral_map(vec2(23, 4), 2) +function keep_score() + local offset = am.current_time() + local score = am.text(string.format("%.2f", am.current_time())) + + win.scene:append(am.translate(-380, 290) ^ score) + win.scene:action(function() + score.text = string.format("%.2f", am.current_time()) + end) +end + + +function show_hex_coords() + local hex = pixel_to_hex(win:mouse_position(), vec2(11)) + local off = hex_to_offset(hex) + local coords = am.text(string.format("%d,%d", off.x, off.y)) + win.scene:append(am.translate(380, 280) ^ coords + :action(function() + local hex = pixel_to_hex(win:mouse_position(), vec2(11)) + local off = hex_to_offset(hex) + coords.text = string.format("%d,%d", off.x, off.y) + end)) +end + + +function explosion(position, size, color, color_var, sound) end + +-- ensure home-base is somewhat of an open area. +function find_home(preferred_radius) + home = spiral_map(vec2(23, 4), preferred_radius or 2) home_node = am.group() + repeat local happy = true for i,h in pairs(home) do - local elevation = hash_retrieve(map, h) if not elevation then -- hex not in map @@ -31,12 +62,12 @@ function find_home() happy = false elseif not happy then - home = spiral_map(h, 2) + home = spiral_map(vec2(23, 4), preferred_radius or 2) home_node = am.group() else local center = hex_to_pixel(h, vec2(11)) - local color = vec4(0.5) + local color = vec4(1, 0, 0.5, 1) local node = am.circle(center, 4, color, 4) home_node:append(node) end @@ -46,6 +77,7 @@ function find_home() end +-- map elevation to appropriate tile color. function color_at(elevation) if elevation < -0.5 then -- Lowest Elevation : Impassable return vec4(0.10, 0.30, 0.40, (elevation + 1.4) / 2 + 0.2) @@ -58,41 +90,53 @@ function color_at(elevation) elseif elevation < 1 then -- Highest Elevation : Impassable return vec4(0.15, 0.30, 0.20, (elevation + 1.0) / 2 + 0.2) - else return vec4(0.5, 0.5, 0.5, 1) end end -function draw_(map) +function cartograph() + map = rectangular_map(46, 33); math.randomseed(map.seed) world = am.group() for hex,elevation in pairs(map) do + -- subtle shading for map edges. unnecessary, really. local off = hex_to_offset(hex) local mask = vec4(0, 0, 0, math.max(((off.x - 23.5) / 46) ^ 2, ((-off.y - 16.5) / 32) ^ 2)) local color = color_at(elevation) - mask + local node = am.circle(hex_to_pixel(hex, vec2(11)), 11, vec4(0), 6) - :action(am.tween(5, {color = color}, am.ease.out(am.ease.hyperbola))) - world:append(node:tag(tostring(hex))) + :action(am.tween(5, {color=color}, am.ease.out(am.ease.hyperbola))) + world:append(node) end - world:append(find_home()) - return am.translate(-278, -318) ^ world:tag"world" + if bias == "right" then + win.scene:prepend(am.translate(-278, -318) ^ world) + win.scene:action(am.tween(win.scene"curtain", 1, {x2 = -268}, am.ease.bounce)) + + elseif bias == "left" then + win.scene:prepend(am.translate(-480, -318) ^ world) + win.scene:action(am.tween(win.scene"curtain", 1, {x1 = 268}, am.ease.bounce)) + + else + error("invalid bias") + end + world:action(spawner) end +-- determines when, where, and how often to spawn mobs. function spawner(world) - - if math.random(10) == 1 then -- chance to spawn + if math.random(25) == 1 then -- chance to spawn local spawn_position repeat -- ensure we spawn on an random tile along the map's edges local x, y = math.random(46), math.random(33) if math.random() < 0.5 then - x = math.random(0, 1) * 47 + x = math.random(0, 1) * 46 else y = math.random(0, 1) * 33 end @@ -102,37 +146,69 @@ function spawner(world) local e = hash_retrieve(map, spawn_position) until e and e < 0.5 and e > -0.5 - local mob = am.circle(hex_to_pixel(spawn_position, vec2(11)), 4, vec4(1), 6) - :action(coroutine.create(function(mob) - local dead = false - repeat - local neighbours = hex_neighbours(pixel_to_hex(mob.center, vec2(11))) - local candidates = {} - for _,h in pairs(neighbours) do - - local e = hash_retrieve(map, h) - if e and e < 0.5 and e > -0.5 then - table.insert(candidates, h) - end - end - local move = candidates[math.random(#candidates)] - am.wait(am.tween(mob, 1, {center=hex_to_pixel(move, vec2(11))})) - until dead - end)) - world:append(mob) + local mob + if bias == "right" then + mob = am.translate(-278, -318) ^ am.circle(hex_to_pixel(spawn_position, vec2(11)), 4) + + elseif bias == "left" then + mob = am.translate(-480, -318) ^ am.circle(hex_to_pixel(spawn_position, vec2(11)), 4) + end + world:append(mob"circle":action(coroutine.create(live))) end end -function game_init(seed) - local bg = am.rect(-480, 300, -268, -300, vec4(0.12, 0.3, 0.3, 1)) - map = rectangular_map(46, 33) +-- This function is the coroutine that represents the life-cycle of a mob. +function live(mob) + local dead = false - math.randomseed(map.seed) - win.scene = am.group(draw_(map):action(spawner), bg) -end + local visited = {}; visited[mob.center] = true + + -- begin life + repeat + local neighbours = hex_neighbours(pixel_to_hex(mob.center, vec2(11))) + local candidates = {} + + -- get list of candidates: hex positions to consider moving to. + for _,h in pairs(neighbours) do + + local e = hash_retrieve(map, h) + if e and e < 0.5 and e > -0.5 then + if not hash_retrieve(visited, h) then + table.insert(candidates, h) + end + end + end + + -- choose where to move. manhattan distance closest to goal is chosen. + local move = candidates[1] + for _,h in pairs(candidates) do + if math.distance(h, home.center) < math.distance(move, home.center) then + move = h + end + end + + if not move then return true --error("can't find anywhere to move to") + end -- bug + local speed = (hash_retrieve(map, move)) ^ 2 + 0.5 + am.wait(am.tween(mob, speed, {center=hex_to_pixel(move, vec2(11))})) + visited[move] = true + if move == home.center then dead = true end + until dead + explosion(mob.center) +end -- -game_init() +function init() + local bg = am.rect(win.left, win.top, win.right, win.bottom, vec4(0.12, 0.3, 0.3, 1)) + :tag"curtain" + + -- -480, 300, -268, -300 + + win.scene = am.group(bg) + cartograph() +end + +init()