|
|
@ -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 ----- |
|
|
|
============================================================================]]-- |