@ -1,8 +1,6 @@
-- Rounds Numbers.
-- Rounds Numbers.
local function round ( n )
return n % 1 >= 0.5 and math.ceil ( n ) or math.floor ( n )
end
local function round ( n ) return n % 1 >= 0.5 and math.ceil ( n ) or math.floor ( n ) end
--[[============================================================================
--[[============================================================================
----- HEX CONSTANTS AND UTILITY FUNCTIONS -----
----- HEX CONSTANTS AND UTILITY FUNCTIONS -----
@ -13,59 +11,53 @@ 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
function hex_not_equals ( a , b ) return not hex_equals ( a , b ) end
-- All Possible Vector Directions from a Given Hex by Edge
local HEX_DIRECTIONS = { vec2 ( 0 , 1 ) , vec2 ( 1 , 0 ) , vec2 ( 1 , - 1 ) ,
vec2 ( 0 , - 1 ) , vec2 ( - 1 , 0 ) , vec2 ( - 1 , 1 ) }
-- 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 ) }
-- Return Hex Vector Direction via Integer Index |direction|.
-- Return Hex Vector Direction via Integer Index |direction|
function hex_direction ( direction )
function hex_direction ( direction )
return HEX_DIRECTIONS [ ( direction % 6 ) % 6 + 1 ]
end
return HEX_DIRECTIONS [ ( direction % 6 ) % 6 + 1 ] end
-- Return Hexagon Adjacent to |hex| in Integer Index |direction|.
-- Return Hexagon Adjacent to |hex| in Integer Index |direction|
function hex_neighbour ( hex , direction )
function hex_neighbour ( hex , direction )
return hex + HEX_DIRECTIONS [ ( direction % 6 ) % 6 + 1 ]
end
-- Return Hex 60deg away to the Left; Counter-Clockwise
function hex_rotate_left ( hex )
return vec2 ( hex.x + hex.y , - hex.x )
end
return hex + HEX_DIRECTIONS [ ( direction % 6 ) % 6 + 1 ] end
-- Return Hex 60deg away to the Right; Clockwise
function hex_rotate_right ( hex )
return vec2 ( - hex.y , hex.x + hex.y )
-- Collect All 6 Neighbours in a Table
function hex_neighbours ( hex )
local neighbours = { }
for i = 1 , 6 do
table.insert ( neighbours , hex_neighbour ( hex , i ) )
end
return neighbours
end
end
-- NOT a General 3D Vector Round - Only Returns a vec2!
-- Returns a vec2 Which is the Nearest |x, y| to Float Trio |x, y, z|
local function hex_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 )
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 )
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
end
--[[==========================================================================--
--[[==========================================================================--
----- ORIENTATION & LAYOUT -----
----- ORIENTATION & LAYOUT -----
============================================================================ ] ] --
============================================================================ ] ] --
-- Forward & Inverse Matrices used for the Flat Orientation
-- 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 ) ,
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 ) ,
W = mat2 ( 2.0 / 3.0 , 0.0 , - 1.0 / 3.0 , 3.0 ^ 0.5 / 3.0 ) ,
@ -76,266 +68,221 @@ 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 ) ,
W = mat2 ( 3.0 ^ 0.5 / 3.0 , - 1.0 / 3.0 , 0.0 , 2.0 / 3.0 ) ,
angle = 0.5 }
angle = 0.5 }
-- Hex to Screen -- Orientation Must be Either POINTY or FLAT
-- Hex to Screen -- Orientation Must be Either POINTY or FLAT
function hex_to_pixel ( hex , size , orientation_M )
function hex_to_pixel ( hex , size , orientation_M )
local M = orientation_M or FLAT.M
local M = orientation_M or FLAT.M
local x = ( M [ 1 ] [ 1 ] * hex [ 1 ] + M [ 1 ] [ 2 ] * hex [ 2 ] ) * size [ 1 ]
local y = ( M [ 2 ] [ 1 ] * hex [ 1 ] + M [ 2 ] [ 2 ] * hex [ 2 ] ) * size [ 2 ]
local x = ( M [ 1 ] [ 1 ] * hex [ 1 ] + M [ 1 ] [ 2 ] * hex [ 2 ] ) * size [ 1 ]
local y = ( M [ 2 ] [ 1 ] * hex [ 1 ] + M [ 2 ] [ 2 ] * hex [ 2 ] ) * size [ 2 ]
return vec2 ( x , y )
return vec2 ( x , y )
end
end
-- Screen to Hex -- Orientation Must be Either POINTY or FLAT
-- Screen to Hex -- Orientation Must be Either POINTY or FLAT
function pixel_to_hex ( pix , size , orientation_W )
function pixel_to_hex ( pix , size , orientation_W )
local W = orientation_W or FLAT.W
local W = orientation_W or FLAT.W
local pix = pix / size
local pix = pix / size
local x = W [ 1 ] [ 1 ] * pix [ 1 ] + W [ 1 ] [ 2 ] * pix [ 2 ]
local y = W [ 2 ] [ 1 ] * pix [ 1 ] + W [ 2 ] [ 2 ] * pix [ 2 ]
local x = W [ 1 ] [ 1 ] * pix [ 1 ] + W [ 1 ] [ 2 ] * pix [ 2 ]
local y = W [ 2 ] [ 1 ] * pix [ 1 ] + W [ 2 ] [ 2 ] * pix [ 2 ]
return hex_round ( x , y , - x - y )
return hex_round ( x , y , - x - y )
end
end
-- TODO test, learn am.draw
-- TODO test, learn am.draw
function hex_corner_offset ( corner , size , orientation_angle )
function hex_corner_offset ( corner , size , orientation_angle )
local angle = 2.0 * math.pi * orientation_angle or FLAT.angle + corner / 6
return vec2 ( size [ 1 ] * math.cos ( angle ) ,
size [ 2 ] * math.sin ( angle ) )
local angle = 2.0 * math.pi * orientation_angle or FLAT.angle + corner / 6
return vec2 ( size [ 1 ] * math.cos ( angle ) , size [ 2 ] * math.sin ( angle ) )
end
end
-- TODO this thing
-- TODO test t his thing
function hex_corners ( hex , size , orientation )
function hex_corners ( hex , size , orientation )
local corners = { }
local corners = { }
local center = hex_to_pixel ( hex , size , orientation )
for i = 0 , 5 do
local offset = hex_corner_offset ( i , size , orientation )
table.insert ( corners , center + offset )
end
return corners
end
end
-- Offset Coordinates are Useful for UI-Implementations
-- Offset Coordinates Look Nice / are Useful for UI-Implementations
function hex_to_offset ( hex )
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 c oordinates
-- ... Back to Cube C oordinates
function offset_to_hex ( off )
function offset_to_hex ( off )
return vec2 ( off [ 1 ] , off [ 2 ] - off [ 1 ] * ( off [ 1 ] % 2 ) / 2 )
end
return vec2 ( off [ 1 ] , off [ 2 ] - math.floor ( ( off [ 1 ] - 1 * ( off [ 1 ] % 2 ) ) ) / 2 ) end
--[[============================================================================
--[[============================================================================
----- MAPS & STORAGE -----
----- MAPS & STORAGE -----
You are not to draw using the coordinates stored in your map .
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 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
use am.draw - TODO , haven ' t used am.draw yet.
Maps have metatables containing information about their dimensions , and
seed ( if applicable ) , so you can retrieve information about maps after they
are created .
----- NOISE -----
To simplify terrain generation , unordered , hash - like maps automatically
calculate and store seeded simplex noise as their values . You can provide
a seed if you wish . The default is a randomized seed .
TODO Pointy Hex testing and support .
============================================================================ ] ] --
============================================================================ ] ] --
-- Returns Ordered Ring-Shaped Map of |radius| from |center|
-- Returns Ordered Ring-Shaped Map of |radius| from |center|
function ring_map ( center , radius )
function ring_map ( center , radius )
local map = { }
local walk = center + HEX_DIRECTIONS [ 6 ] * radius
for i = 1 , 6 do
for j = 1 , radius do
table.insert ( map , walk )
walk = hex_neighbour ( walk , i )
end
end
setmetatable ( map , { __index = { center = center , radius = radius } } )
return map
local map = { }
local walk = center + HEX_DIRECTIONS [ 6 ] * radius
for i = 1 , 6 do
for j = 1 , radius do
table.insert ( map , walk )
walk = hex_neighbour ( walk , i )
end
end
setmetatable ( map , { __index = { center = center , radius = radius } } )
return map
end
end
-- Returns Ordered Spiral Hexagonal Map of |radius| Rings from |center|
-- Returns Ordered Spiral Hexagonal Map of |radius| Rings from |center|
function spiral_map ( center , radius )
function spiral_map ( center , radius )
local map = { center }
local map = { center }
for i = 1 , radius do
table.append ( map , ring_map ( center , i ) )
end
setmetatable ( map , { __index = { center = center , radius = radius } } )
return map
end
for i = 1 , radius do
table.append ( map , ring_map ( center , i ) )
end
setmetatable ( map , { __index = { center = center , radius = radius } } )
return map
-- 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
return n
end
end
return nil
end
end
-- Returns Unordered Parallelogram-Shaped Map of |width| and |height| with Simplex Noise
-- Returns Unordered Parallelogram-Shaped Map of |width| and |height| with Simplex Noise
function parallelogram_map ( width , height , seed )
function parallelogram_map ( width , height , seed )
local seed = seed or math.random ( width * height )
local map = { }
for i = 0 , width do
for j = 0 , height do
-- Calculate Noise
local idelta = i / width
local jdelta = j / height
local noise = 0
for oct = 1 , 6 do
local f = 1 / 4 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * width , jdelta + seed * height )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j ) ] = noise -- Straightforward Iteration Produces a Parallelogram
end
end
setmetatable ( map , { __index = { width = width , height = height , seed = seed } } )
return map
local seed = seed or math.random ( width * height )
local map = { }
for i = 0 , width do
for j = 0 , height do
-- Calculate Noise
local idelta = i / width
local jdelta = j / height
local noise = 0
for oct = 1 , 6 do
local f = 1 / 4 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * width , jdelta + seed * height )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j ) ] = noise
end
end
setmetatable ( map , { __index = { width = width , height = height , seed = seed } } )
return map
end
end
-- Returns Unordered Triangular Map of |size| with Simplex Noise
-- Returns Unordered Triangular (Equilateral) Map of |size| with Simplex Noise
function triangular_map ( size , seed )
function triangular_map ( size , seed )
local seed = seed or math.random ( size )
local map = { }
for i = 0 , size do
for j = size - i , size do
-- Generate Noise
local idelta = i / size
local jdelta = j / size
local noise = 0
for oct = 1 , 6 do
local f = 1 / 3 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * size , jdelta + seed * size )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j ) ] = noise
end
end
setmetatable ( map , { __index = { size = size , seed = seed } } )
return map
local seed = seed or math.random ( size * math.cos ( size ) / 2 )
local map = { }
for i = 0 , size do
for j = size - i , size do
-- Generate Noise
local idelta = i / size
local jdelta = j / size
local noise = 0
for oct = 1 , 6 do
local f = 1 / 3 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * size , jdelta + seed * size )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j ) ] = noise
end
end
setmetatable ( map , { __index = { size = size , seed = seed } } )
return map
end
end
-- Returns Unordered Hexagonal Map of |radius| with Simplex Noise
-- Returns Unordered Hexagonal Map of |radius| with Simplex Noise
function hexagonal_map ( radius , seed )
function hexagonal_map ( radius , seed )
local seed = seed or math.random ( radius * 2 + 1 )
local map = { }
for i = - radius , radius do
local j1 = math.max ( - radius , - i - radius )
local j2 = math.min ( radius , - i + radius )
for j = j1 , j2 do
-- Calculate Noise
local idelta = i / radius
local jdelta = j / radius
local noise = 0
for oct = 1 , 6 do
local f = 2 / 3 ^ oct -- NOTE, for some reason, I found 2/3 produces better looking noise maps. As far as I am aware, this is weird.
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * radius , jdelta + seed * radius )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j ) ] = noise
end
end
setmetatable ( map , { __index = { radius = radius , seed = seed } } )
return map
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 )
for j = j1 , j2 do
-- Calculate Noise
local idelta = i / radius
local jdelta = j / radius
local noise = 0
for oct = 1 , 6 do
local f = 2 / 3 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * radius , jdelta + seed * radius )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j ) ] = noise
end
end
setmetatable ( map , mt )
return map
end
end
-- Returns Unordered Rectangular Map of |width| and |height| with Simplex Noise
-- Returns Unordered Rectangular Map of |width| and |height| with Simplex Noise
function rectangular_map ( width , height , seed )
function rectangular_map ( width , height , seed )
local seed = seed or math.random ( width * height )
local map = { }
for i = 0 , width do
for j = 0 , height do
-- Calculate Noise
local idelta = i / width
local jdelta = j / height
local noise = 0
for oct = 1 , 6 do
local f = 2 / 3 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * width , jdelta + seed * height )
noise = noise + f * math.simplex ( pos * l )
end
-- Store Hex in the Map Paired with its Associated Noise Value
map [ vec2 ( i , j - math.floor ( i / 2 ) ) ] = noise
end
end
setmetatable ( map , { __index = { width = width , height = height , seed = seed } } )
return map
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
-- Begin to Calculate Noise
local idelta = i / width
local jdelta = j / height
local noise = 0
for oct = 1 , 6 do
local f = 2 / 3 ^ oct
local l = 2 ^ oct
local pos = vec2 ( idelta + seed * width , jdelta + seed * height )
noise = noise + f * math.simplex ( pos * l )
end
map [ vec2 ( i , j - math.floor ( i / 2 ) ) ] = noise
end
end
setmetatable ( map , mt )
return map
end
end
--[[==========================================================================--
--[[==========================================================================--
----- PATHFINDING -----
----- PATHFINDING -----
============================================================================ ] ] --
============================================================================ ] ] --
-- big ol' TODO
-- first try
function search ( map , start )
local neighbours
for i = 1 , 6 do
neighbours [ # neighbours + 1 ] = hex_neighbour ( start , i )
end
end
--
function breadth_first_search ( map , start )
local frontier = { start }
local visited = { start = true }
while next ( frontier ) ~= nil do
local current = next ( frontier )
local neighbours
for i = 1 , 6 do
neighbours [ # neighnours + 1 ] = hex_neighbour ( current , i )
end
for _ , n in neighbours do
if visited [ n ] ~= true then
visited [ n ] = true
end
end
end
end