You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
9.4 KiB
314 lines
9.4 KiB
|
|
--[[============================================================================
|
|
----- GENERALLY USEFUL FUNCTIONS -----
|
|
============================================================================]]--
|
|
|
|
-- rounds numbers. would've been cool to have math.round in lua.
|
|
local function round(n)
|
|
return n % 1 >= 0.5 and math.ceil(n) or math.floor(n)
|
|
end
|
|
|
|
--[[============================================================================
|
|
----- HEX CONSTANTS AND UTILITY FUNCTIONS -----
|
|
============================================================================]]--
|
|
|
|
-- all possible vector directions from a given hex by edge
|
|
local CUBE_DIRECTIONS = {vec2( 0 , 1),
|
|
vec2( 1 , 0),
|
|
vec2( 1 , -1),
|
|
vec2( 0 , -1),
|
|
vec2(-1 , 0),
|
|
vec2(-1 , 1)}
|
|
|
|
-- return hex vector direction via integer index |direction|.
|
|
function cube_direction(direction)
|
|
return CUBE_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]
|
|
end
|
|
|
|
-- return cube coords at location 60deg away to the left; counter-clockwise
|
|
function cube_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)
|
|
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 rx = round(x)
|
|
local ry = round(y)
|
|
local rz = round(z) or round(-x - y)
|
|
|
|
local xdelta = math.abs(rx - x)
|
|
local ydelta = math.abs(ry - y)
|
|
local zdelta = math.abs(rz - z or round(-x - y))
|
|
|
|
if xdelta > ydelta and xdelta > zdelta then
|
|
rx = -ry - rz
|
|
elseif ydelta > zdelta then
|
|
ry = -rx - rz
|
|
else
|
|
rz = -rx - ry
|
|
end
|
|
return vec2(rx, ry)
|
|
end
|
|
|
|
--[[============================================================================
|
|
----- ORIENTATION & LAYOUT -----
|
|
============================================================================]]--
|
|
|
|
-- forward & inverse matrices used for the flat orientation
|
|
local FLAT = {M = mat2(3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5 ),
|
|
W = mat2(2.0/3.0, 0.0, -1.0/3.0 , 3.0^0.5/3.0),
|
|
start_angle = 0.0}
|
|
|
|
-- forward & inverse matrices used for the pointy orientation
|
|
local POINTY = {M = mat2(3.0^0.5, 3.0^0.5/2.0, 0.0, 3.0/2.0),
|
|
W = mat2(3.0^0.5/3.0, -1.0/3.0, 0.0, 2.0/3.0),
|
|
start_angle = 0.5}
|
|
|
|
-- stores layout: information that does not pertain to map shape
|
|
function layout(origin, size, orientation)
|
|
return {origin = origin or vec2(0),
|
|
size = size or vec2(11),
|
|
orientation = orientation or FLAT}
|
|
end
|
|
|
|
-- hex to screen
|
|
function cube_to_pixel(cube, 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]
|
|
|
|
return vec2(x + layout.origin[1], y + layout.origin[2])
|
|
end
|
|
|
|
-- screen to hex
|
|
function pixel_to_cube(pix, layout)
|
|
local W = layout.orientation.W
|
|
|
|
local pix = (pix - layout.origin) / layout.size
|
|
|
|
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)
|
|
end
|
|
|
|
-- TODO test, learn am.draw
|
|
function hex_corner_offset(corner, layout)
|
|
local angle = 2.0 * math.pi * layout.orientation.start_angle + corner / 6
|
|
return vec2(layout.size[1] * math.cos(angle),
|
|
layout.size[2] * math.sin(angle))
|
|
end
|
|
|
|
-- TODO this thing
|
|
function hex_corners(hex, layout)
|
|
local corners = {}
|
|
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)
|
|
end
|
|
|
|
-- back to cube coordinates
|
|
function offset_to_cube(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.
|
|
|
|
If you wish to draw a hexagon to the screen, you must first use cube_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
|
|
use am.draw - TODO, haven't used am.draw yet.
|
|
|
|
Information about the maps' dimensions are stored in a metatable, so you can
|
|
retrieve details about maps after they are created.
|
|
|
|
----- NOISE -----
|
|
To simplify terrain generation, unordered, hash-like maps automatically
|
|
calculate and store perlin 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
|
|
of do, just not always nicely.
|
|
|
|
============================================================================]]--
|
|
----- ORDERED MAPS -----
|
|
|
|
-- returns ordered ring-shaped map of |radius| from |center|.
|
|
function ring_map(center, radius)
|
|
local map = {}
|
|
local mt = {__index={center=center, radius=radius}}
|
|
|
|
setmetatable(map, mt)
|
|
|
|
local walk = center + CUBE_DIRECTIONS[6] * radius
|
|
|
|
for i = 1, 6 do
|
|
for j = 1, radius do
|
|
table.insert(map, walk)
|
|
walk = cube_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|.
|
|
|
|
function spiral_map(center, radius)
|
|
local map = {center}
|
|
local mt = {__index={center=center, radius=radius}}
|
|
|
|
setmetatable(map, mt)
|
|
|
|
for i = 1, radius do
|
|
table.append(map, ring_map(center, i))
|
|
end
|
|
return map
|
|
end
|
|
|
|
----- UNORDERED, HASH-LIKE MAPS -----
|
|
|
|
-- returns unordered parallelogram-shaped map of |width| and |height| with perlin noise
|
|
function parallelogram_map(width, height)
|
|
local map = {}
|
|
local mt = {__index={width=width, height=height}}
|
|
|
|
setmetatable(map, mt)
|
|
|
|
for i = 0, width do
|
|
for j = 0, height do
|
|
map[vec2(i, j)] = true
|
|
end
|
|
end
|
|
return map
|
|
end
|
|
|
|
-- returns unordered triangular map of |size| with perlin noise
|
|
function triangular_map(size)
|
|
local map = {}
|
|
local mt = {__index={size=size}}
|
|
|
|
setmetatable(map, mt)
|
|
|
|
for i = 0, size do
|
|
for j = size - s, size do
|
|
map[vec2(i, j)] = true
|
|
end
|
|
end
|
|
return map
|
|
end
|
|
|
|
-- returns unordered hexagonal map of |radius| with perlin noise
|
|
function hexagonal_map(radius)
|
|
local map = {}
|
|
local mt = {__index={radius=radius}}
|
|
|
|
setmetatable(map, mt)
|
|
|
|
for i = -radius, radius do
|
|
local j1 = math.max(-radius, -i - radius)
|
|
local j2 = math.min(radius, -i + radius)
|
|
|
|
for j = j1, j2 do
|
|
map[vec2(i, j)] = true
|
|
end
|
|
end
|
|
return map
|
|
end
|
|
|
|
-- returns unordered rectangular map of |width| and |height| with perlin noise
|
|
function rectangular_map(width, height, frequencies)
|
|
|
|
local map = {}
|
|
local mt = {__index={width=width, height=height}}
|
|
local frequencies = frequencies or {1}
|
|
|
|
setmetatable(map, mt)
|
|
|
|
for i = 0, width do
|
|
for j = 0, height do
|
|
|
|
-- 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 + 1/freq * math.perlin(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
|
|
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 -----
|
|
============================================================================]]--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
--[[============================================================================
|
|
----- TESTS -----
|
|
============================================================================]]--
|
|
|
|
function test_all()
|
|
print("it works trust me")
|
|
end
|
|
|