From 0df10e2796995221338bc44ae41b8a31467bd328 Mon Sep 17 00:00:00 2001 From: churchianity Date: Sat, 9 Mar 2019 19:12:25 -0500 Subject: [PATCH] made a /src folder --- hex.lua => src/hex.lua | 161 +++++++++++++++++++++------------------ main.lua => src/main.lua | 96 +++++++++++++++-------- 2 files changed, 150 insertions(+), 107 deletions(-) rename hex.lua => src/hex.lua (64%) rename main.lua => src/main.lua (66%) diff --git a/hex.lua b/src/hex.lua similarity index 64% rename from hex.lua rename to src/hex.lua index 0d62931..81563ef 100644 --- a/hex.lua +++ b/src/hex.lua @@ -1,6 +1,6 @@ --[[============================================================================ - ----- GENERALLY USEFUL FUNCTIONS ----- + ----- GENERALLY USEFUL FUNCTIONS ----- ============================================================================]]-- -- rounds numbers. would've been cool to have math.round in lua. @@ -9,11 +9,11 @@ local function round(n) end --[[============================================================================ - ----- HEX CONSTANTS AND UTILITY FUNCTIONS ----- + ----- HEX CONSTANTS AND UTILITY FUNCTIONS ----- ============================================================================]]-- -- all possible vector directions from a given hex by edge -local CUBE_DIRECTIONS = {vec2( 0 , 1), +local HEX_DIRECTIONS = {vec2( 0 , 1), vec2( 1 , 0), vec2( 1 , -1), vec2( 0 , -1), @@ -21,27 +21,27 @@ local CUBE_DIRECTIONS = {vec2( 0 , 1), vec2(-1 , 1)} -- return hex vector direction via integer index |direction|. -function cube_direction(direction) - return CUBE_DIRECTIONS[(direction % 6) % 6 + 1] +function hex_direction(direction) + return HEX_DIRECTIONS[(direction % 6) % 6 + 1] end -- return hexagon adjacent to |hex| in integer index |direction|. -function cube_neighbour(hex, direction) - return hex + CUBE_DIRECTIONS[(direction % 6) % 6 + 1] +function hex_neighbour(hex, direction) + return hex + HEX_DIRECTIONS[(direction % 6) % 6 + 1] end -- return cube coords at location 60deg away to the left; counter-clockwise -function cube_rotate_left(hex) +function hex_rotate_left(hex) return vec2(hex.x + hex.y, -hex.x) end -- return cube coords at location 60deg away to the right; clockwise -function cube_rotate_right(hex) +function hex_rotate_right(hex) return vec2(-hex.y, hex.x + hex.y) end -- rounds a float coordinate trio |x, y, z| to nearest integer coordinate trio -local function cube_round(x, y, z) +local function hex_round(x, y, z) local rx = round(x) local ry = round(y) local rz = round(z) or round(-x - y) @@ -61,7 +61,7 @@ local function cube_round(x, y, z) end --[[============================================================================ - ----- ORIENTATION & LAYOUT ----- + ----- ORIENTATION & LAYOUT ----- ============================================================================]]-- -- forward & inverse matrices used for the flat orientation @@ -82,17 +82,17 @@ function layout(origin, size, orientation) end -- hex to screen -function cube_to_pixel(cube, layout) +function hex_to_pixel(hex, layout) local M = layout.orientation.M - local x = (M[1][1] * cube[1] + M[1][2] * cube[2]) * layout.size[1] - local y = (M[2][1] * cube[1] + M[2][2] * cube[2]) * layout.size[2] + local x = (M[1][1] * hex[1] + M[1][2] * hex[2]) * layout.size[1] + local y = (M[2][1] * hex[1] + M[2][2] * hex[2]) * layout.size[2] return vec2(x + layout.origin[1], y + layout.origin[2]) end -- screen to hex -function pixel_to_cube(pix, layout) +function pixel_to_hex(pix, layout) local W = layout.orientation.W local pix = (pix - layout.origin) / layout.size @@ -100,7 +100,7 @@ function pixel_to_cube(pix, layout) local s = W[1][1] * pix[1] + W[1][2] * pix[2] local t = W[2][1] * pix[1] + W[2][2] * pix[2] - return cube_round(s, t, -s - t) + return hex_round(s, t, -s - t) end -- TODO test, learn am.draw @@ -116,23 +116,22 @@ function hex_corners(hex, layout) end -- offset coordinates are prettier to look at -function cube_to_offset(cube) - return vec2(cube[1], -cube[1] - cube[2] + (cube[1] + (cube[1] % 2)) / 2) +function hex_to_offset(hex) + return vec2(hex[1], -hex[1] - hex[2] + (hex[1] + (hex[1] % 2)) / 2) end -- back to cube coordinates -function offset_to_cube(off) +function offset_to_hex(off) return vec2(off[1], off[2] - off[1] * (off[1] % 2) / 2) end --[[============================================================================ ----- MAPS & STORAGE ----- - MAPS STORE CUBE COORDINATES. MAPS STORE CUBE COORDINATES. MAPS STORE CUBE COOR This means, you are not to draw using the coordinates stored in your map. - You are to draw using the cube_to_pixel of those coordinates. + You are to draw using the hex_to_pixel of those coordinates. - If you wish to draw a hexagon to the screen, you must first use cube_to_pixel + If you wish to draw a hexagon to the screen, you must first use hex_to_pixel to retrieve the center of the hexagon on each set of cube coordinates stored in your map. Then, depending on how you are going to draw, either call am.circle with |sides| = 6, or gather the vertices with hex_corners and @@ -143,17 +142,16 @@ end ----- NOISE ----- To simplify terrain generation, unordered, hash-like maps automatically - calculate and store perlin noise as their values. You can modify the nature + calculate and store simplex noise as their values. You can modify the nature of the noise by providing different |frequencies| as a tables of values, for example: {1, 2, 4, 8} or {1, 0.5, 0.25, 0.125}. These just increase the complexity of the curvature of the noise. The default is {1}. ----- TODO ----- - TODO make all functions work regardless of layout. as it stands, they kind + make all functions work regardless of layout. as it stands, they kind of do, just not always nicely. ============================================================================]]-- ------ ORDERED MAPS ----- -- returns ordered ring-shaped map of |radius| from |center|. function ring_map(center, radius) @@ -162,21 +160,18 @@ function ring_map(center, radius) setmetatable(map, mt) - local walk = center + CUBE_DIRECTIONS[6] * radius + local walk = center + HEX_DIRECTIONS[6] * radius for i = 1, 6 do for j = 1, radius do table.insert(map, walk) - walk = cube_neighbour(walk, i) + walk = hex_neighbour(walk, i) end end return map end --- returns ordered hexagonal map of |radius| rings from |center|. --- the only difference between spiral_map and hexagonal_map is that --- spiral_map is ordered, in a spiral path from the |center|. - +-- returns ordered spiral hexagonal map of |radius| rings from |center|. function spiral_map(center, radius) local map = {center} local mt = {__index={center=center, radius=radius}} @@ -189,42 +184,67 @@ function spiral_map(center, radius) return map end ------ UNORDERED, HASH-LIKE MAPS ----- - --- returns unordered parallelogram-shaped map of |width| and |height| with perlin noise -function parallelogram_map(width, height) +-- returns unordered parallelogram-shaped map of |width| and |height| with simplex noise +function parallelogram_map(width, height, frequencies) local map = {} - local mt = {__index={width=width, height=height}} + local mt = {__index={width=width, height=height, frequencies=frequencies}} + local frequencies = frequencies or {1} setmetatable(map, mt) for i = 0, width do for j = 0, height do - map[vec2(i, j)] = true + + -- calculate noise + local idelta = assert(i / width, "width must be greater than 0") + local jdelta = assert(j / height, "height must be greater than 0") + local noise = 0 + + for _,freq in pairs(frequencies) do + noise = noise + + assert(1/freq, "frequencies must be non-zero") + * math.simplex(vec2(freq * idelta, freq * jdelta)) + end + + -- straightforward iteration produces a parallelogram + map[vec2(i, j)] = noise end end return map end --- returns unordered triangular map of |size| with perlin noise -function triangular_map(size) +-- returns unordered triangular map of |size| with simplex noise +function triangular_map(size, frequencies) local map = {} - local mt = {__index={size=size}} + local mt = {__index={size=size, frequencies=frequencies}} + local frequencies = frequencies or {1} setmetatable(map, mt) for i = 0, size do - for j = size - s, size do - map[vec2(i, j)] = true + for j = size - i, size do + + -- calculate noise + local idelta = assert(i / size, "size must be greater than 0") + local jdelta = assert(j / -size, "size must be greater than 0") + local noise = 0 + + for _,freq in pairs(frequencies) do + noise = noise + + assert(1/freq, "frequencies must be non-zero") + * math.simplex(vec2(freq * idelta, freq * jdelta)) + end + map[vec2(i, j)] = noise end end return map end --- returns unordered hexagonal map of |radius| with perlin noise -function hexagonal_map(radius) +-- returns unordered hexagonal map of |radius| with simplex noise +function hexagonal_map(radius, frequencies) local map = {} - local mt = {__index={radius=radius}} + local mt = {__index={radius=radius, frequencies=frequencies}} + local frequencies = frequencies or {1} setmetatable(map, mt) @@ -233,17 +253,30 @@ function hexagonal_map(radius) local j2 = math.min(radius, -i + radius) for j = j1, j2 do - map[vec2(i, j)] = true + + -- calculate noise + local idelta = assert(i / radius*2, "radius must be greater than 0") + local jdelta = assert(j / (j2-j1), "radius must be greater than 0") + local noise = 0 + + for _,freq in pairs(frequencies) do + noise = noise + + assert(1/freq, "frequencies must be non-zero") + * math.perlin(vec2(freq * idelta, freq * jdelta)) + end + + -- populate + map[vec2(i, j)] = noise end end return map end --- returns unordered rectangular map of |width| and |height| with perlin noise +-- returns unordered rectangular map of |width| and |height| with simplex noise function rectangular_map(width, height, frequencies) local map = {} - local mt = {__index={width=width, height=height}} + local mt = {__index={width=width, height=height, frequencies=frequencies}} local frequencies = frequencies or {1} setmetatable(map, mt) @@ -257,42 +290,18 @@ function rectangular_map(width, height, frequencies) local noise = 0 for _,freq in pairs(frequencies) do - noise = noise + 1/freq * math.perlin(vec2(freq * idelta, - freq * jdelta)) + noise = noise + + assert(1/freq, "frequencies must be non-zero") + * math.simplex(vec2(freq * idelta, freq * jdelta)) end - -- this is what makes it a rectangle - local hex = vec2(i, j - math.floor(i/2)) - -- store hex in the map paired with its associated noise value - map[hex] = noise + map[vec2(i, j - math.floor(i/2))] = noise end end return map end ---[[============================================================================ - ----- NOISE ----- -============================================================================]]-- - -function simplex_map(frequency, exponent, width, height) - local map = {} - - for i = 0, height do - for j = 0, width do - local idelta = i/width - 0.5 - local jdelta = j/height - 0.5 - map[vec2(i, j)] = math.simplex(idelta, jdelta) - end - end -end - - - - - - - --[[============================================================================ ----- PATHFINDING ----- ============================================================================]]-- diff --git a/main.lua b/src/main.lua similarity index 66% rename from main.lua rename to src/main.lua index fe4834e..e4e98e1 100644 --- a/main.lua +++ b/src/main.lua @@ -1,10 +1,8 @@ require"hex" -require"util" -require"sprites" --[[============================================================================ - ----- COLOR CONSTANTS ----- + ----- COLOR CONSTANTS ----- ============================================================================]]-- local EIGENGRAU = vec4(0.08, 0.08, 0.11, 1) @@ -48,21 +46,23 @@ am.ascii_color_map = } --[[============================================================================ - ----- SETUP ----- + ----- SETUP ----- ============================================================================]]-- local win = am.window -{ -- base resolution = 3/4 * WXGA standard 16:10 +{ + -- base resolution = 3/4 * WXGA standard 16:10 width = 1280 * 3/4, -- 960px height = 800 * 3/4, -- 600px clear_color = BASE03 } -local map = rectangular_map(45, 31) -local layout = layout(vec2(-268, win.top - 10)) +local map = rectangular_map(45, 31, {2, 4, 8}) +local layout = layout(vec2(-268, win.bottom)) +local home = hex_to_pixel(vec2(23, 4), layout) --[[============================================================================ - ----- SCENE GRAPH / NODES ----- + ----- SCENE GRAPH / NODES ----- ============================================================================]]-- local panel; local world; local game --[[ @@ -82,17 +82,19 @@ local backdrop; local menu; local title --[[ menu --[[============================================================================ - ----- FUNCTIONS ----- + ----- FUNCTIONS ----- ============================================================================]]-- + +-- function keep_time() local offset = am.current_time() - world:action(function() - world:remove("time") + game:action(function() + game:remove("time") local time_str = string.format("%.2f", am.current_time() - offset) - world:append( + game:append( am.translate(-374, win.top - 10) ^ am.text(time_str):tag"time") end) @@ -104,34 +106,35 @@ function show_coords() game:remove("coords") game:remove("selected") - local hex = pixel_to_cube(win:mouse_position(), layout) - local mouse = cube_to_offset(hex) + local hex = pixel_to_hex(win:mouse_position(), layout) + local mouse = hex_to_offset(hex) -- check mouse is within bounds of game map if mouse.x > 0 and mouse.x < map.width and - mouse.y > 0 and mouse.y < map.height then + -mouse.y > 0 and -mouse.y < map.height then -- north is positive local text = am.text(string.format("%d,%d", mouse.x, mouse.y)) local coords = am.group{ am.translate(win.right - 25, win.top - 10) - ^ am.text(string.format("%d,%d", mouse.x, mouse.y)):tag"coords"} + ^ am.text(string.format("%d,%d", mouse.x,-mouse.y)):tag"coords"} world:append(coords) - local color = vec4(1) - local pix = cube_to_pixel(hex, layout) + local color = vec4(0, 0, 0, 0.2) + local pix = hex_to_pixel(hex, layout) world:append(am.circle(pix, layout.size.x, color, 6):tag"selected") end end) end +-- function title_init() backdrop = am.group{}:tag"backdrop" menu = am.group{}:tag"menu" title = am.group{menu, backdrop}:tag"title" end - +-- function game_init() -- setup nodes world = am.group{}:tag"world" @@ -148,34 +151,65 @@ function game_init() for hex,noise in pairs(map) do -- determine cell color based on noise - local color = vec4((noise + 1) / 2) + local color + + -- impassable + if noise < -0.5 then + color = vec4(0.10, 0.30, 0.20, (noise + 1) / 2) + + -- passable + elseif noise < 0 then + color = vec4(0.10, 0.25, 0.05, (noise + 1.9) / 2) + + -- passable + elseif noise < 0.5 then + color = vec4(0.25, 0.20, 0.10, (noise + 1.9) / 2) + + -- impassable + else + color = vec4(0.10, 0.30, 0.20, (noise + 1) / 2) + end -- determine cell shading mask based on map position - local off = cube_to_offset(hex) - local mask = vec4(0, 0, 0, math.max(((off.x-23)/30)^2, - ((off.y-16)/20)^2)) + local off = hex_to_offset(hex) + local mask = vec4(0, 0, 0, math.max(((off.x-23)/45)^2, + ((-off.y-16)/31)^2)) + color = color - mask + -- determine hexagon center for drawing - local center = cube_to_pixel(hex, layout) + local center = hex_to_pixel(hex, layout) -- prepend hexagon to screen world:prepend(am.circle(center, 11, color, 6):tag(tostring(hex))) - am.wait(am.delay(0.01)) -- fade in bg panel - panel"bg".color = BASE03/am.frame_time + panel"bg".color = BASE03 + + -- sleep + --am.wait(am.delay(0.01)) end + -- home base + world:append(am.translate(home) + ^ am.rotate(0):tag"homer" + ^ am.circle(vec2(0), 22, ORANGE, 3)):tag"home" + + world:append(am.translate(home) + ^ am.rotate(60):tag"homer2" + ^ am.circle(vec2(0), 22, YELLOW, 3)):tag"home" + + world:action(function() + world"homer".angle = am.frame_time / 6 + world"homer2".angle = am.frame_time / 3 + end) show_coords() -- mouse-hover events keep_time() -- scoring - end)) - - -- make it so - win.scene = game + win.scene = game -- make it so end --[[============================================================================ - ----- MAIN ----- + ----- MAIN ----- ============================================================================]]-- game_init()