From 471aab4846aef397d82e8966f5a748c8fe55549b Mon Sep 17 00:00:00 2001 From: Nicholas Hayashi Date: Sat, 20 Feb 2021 13:28:53 -0500 Subject: [PATCH] refactor hexyz --- main.lua | 62 ++++++++++++++- res/shaded_hex1.png | Bin 0 -> 56727 bytes src/extra.lua | 4 - src/game.lua | 30 +++---- src/grid.lua | 92 +++++++++------------ src/gui.lua | 129 ++++++++++++++++++++++-------- src/hexyz.lua | 189 ++++++++++++++++++++++++-------------------- src/mob.lua | 14 ++-- src/projectile.lua | 6 +- src/tower.lua | 19 +++-- texture.lua | 1 + 11 files changed, 330 insertions(+), 216 deletions(-) create mode 100644 res/shaded_hex1.png diff --git a/main.lua b/main.lua index 906da57..a69f87e 100644 --- a/main.lua +++ b/main.lua @@ -26,7 +26,7 @@ do } end --- assets and/or trivial code +-- asset interfaces and/or trivial code require "color" require "sound" require "texture" @@ -42,10 +42,64 @@ require "src/mob" require "src/projectile" require "src/tower" -function main_action() end -function main_scene() end + +function main_action(self) + self"hex_backdrop""rotate".angle = math.wrapf(self"hex_backdrop""rotate".angle - 0.002 * am.delta_time, math.pi*2) +end + +function make_main_scene_toolbelt() + local options = { + { + label = "new game", + action = function(self) end + }, + { + label = "load game", + action = function(self) game_init(am.load_state("save", "json")) end + }, + { + label = "map editor", + action = function(self) log("map editor not implemented") end + }, + { + label = "settings", + action = function(self) end + }, + } + --local map = hex_rectangular_map(10, 20, HEX_ORIENTATION.POINTY) + + return group +end + +function main_scene() + local group = am.group() + + local map = hex_hexagonal_map(30) + local hex_backdrop = (am.rotate(0) ^ am.group()):tag"hex_backdrop" + for i,_ in pairs(map) do + for j,n in pairs(map[i]) do + local color = map_elevation_color(n) + color = color{a=color.a - 0.1} + + local node = am.translate(hex_to_pixel(vec2(i, j), vec2(HEX_SIZE))) + ^ am.circle(vec2(0), HEX_SIZE, vec4(0), 6) + + node"circle":action(am.tween(1, { color = color })) + + hex_backdrop:append(node) + end + end + group:append(hex_backdrop) + + group:append(am.translate(0, 200) ^ am.sprite("res/logo.png")) + group:append(make_main_scene_toolbelt()) + + group:action(main_action) + + return group +end win.scene = am.group() -game_init(nil) +game_init() noglobals() diff --git a/res/shaded_hex1.png b/res/shaded_hex1.png new file mode 100644 index 0000000000000000000000000000000000000000..0a5fe4ebca8cf7721cd8726ca6d6666150fe001e GIT binary patch literal 56727 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�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

=`Sd&Axd{DJ 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 iHK;)iYEXmSf&ULEc}!rgd(ig)0000") + else table.insert(chars, ".") end + elseif k == "slash" then + if shift then table.insert(chars, "?") + else table.insert(chars, "/") end + + -- control characters + elseif k == "backspace" then + -- @NOTE this doesn't preserve the order of chars in the array so if + -- someone presses a the key "a" then the backspace key in the same frame, in that order + -- the backspace occurs first + self"text".text = self"text".text:sub(1, self"text".text:len() - 1) + + elseif k == "tab" then + -- @TODO + + elseif k == "space" then + table.insert(chars, " ") + + elseif k == "capslock" then + -- @OTOD + end + end + for _,c in pairs(chars) do + if not disallowed_chars[c] then + if self"text".text:len() <= max then + self"text".text = self"text".text .. c end end end diff --git a/src/hexyz.lua b/src/hexyz.lua index 87cdc82..8403f77 100644 --- a/src/hexyz.lua +++ b/src/hexyz.lua @@ -1,14 +1,26 @@ +-- this is a single file with no dependencies which is meant to perform a bunch of mathy stuff +-- related to hexagons, grids of them, and pathfinding on them +-- +-- it basically owes its entire existence to this resource: https://www.redblobgames.com/grids/hexagons/ +-- it uses some datatypes internal to the amulet game engine: http://www.amulet.xyz/ +-- (vec2, mat2) +-- and some utility functions not present in your standard lua, like: +-- table.append + if not math.round then math.round = function(n) return math.floor(n + 0.5) end else - log("clobbering a math.round function.") + error("clobbering a math.round function, oopsie!") +end + +if not table.append then end -- wherever 'orientation' appears as an argument, use one of these two, or set a default just below -ORIENTATION = { +HEX_ORIENTATION = { -- Forward & Inverse Matrices used for the Flat Orientation FLAT = { M = mat2(3.0/2.0, 0.0, 3.0^0.5/2.0, 3.0^0.5 ), @@ -23,70 +35,71 @@ ORIENTATION = { } } --- whenver |orientation| appears as an argument, if it isn't provided, this is used instead. -local DEFAULT_ORIENTATION = ORIENTATION.FLAT +-- whenever |orientation| appears as an argument, if it isn't provided, this is used instead. +-- this is useful because most of the time you will only care about one orientation +local HEX_DEFAULT_ORIENTATION = HEX_ORIENTATION.FLAT -- whenever |size| for a hexagon appears as an argument, if it isn't provided, use this -- 'size' here is distance from the centerpoint to any vertex in pixel -local DEFAULT_HEX_SIZE = vec2(20) +local HEX_DEFAULT_SIZE = vec2(20) -- actual width (longest contained horizontal line) of the hexagon function hex_width(size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION - if orientation == ORIENTATION.FLAT then + if orientation == HEX_ORIENTATION.FLAT then return size * 2 - elseif orientation == ORIENTATION.POINTY then + elseif orientation == HEX_ORIENTATION.POINTY then return math.sqrt(3) * size end end -- actual height (tallest contained vertical line) of the hexagon function hex_height(size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION - if orientation == ORIENTATION.FLAT then + if orientation == HEX_ORIENTATION.FLAT then return math.sqrt(3) * size - elseif orientation == ORIENTATION.POINTY then + elseif orientation == HEX_ORIENTATION.POINTY then return size * 2 end end -- returns actual width and height of a hexagon given it's |size| which is the distance from the centerpoint to any vertex in pixels function hex_dimensions(size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION return vec2(hex_width(size, orientation), hex_height(size, orientation)) end -- distance between two horizontally adjacent hexagon centerpoints function hex_horizontal_spacing(size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION - if orientation == ORIENTATION.FLAT then + if orientation == HEX_ORIENTATION.FLAT then return hex_width(size, orientation) * 3/4 - elseif orientation == ORIENTATION.POINTY then + elseif orientation == HEX_ORIENTATION.POINTY then return hex_height(size, orientation) end end -- distance between two vertically adjacent hexagon centerpoints function hex_vertical_spacing(size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION - if orientation == ORIENTATION.FLAT then + if orientation == HEX_ORIENTATION.FLAT then return hex_height(size, orientation) - elseif orientation == ORIENTATION.POINTY then + elseif orientation == HEX_ORIENTATION.POINTY then return hex_width(size, orientation) * 3/4 end end -- returns the distance between adjacent hexagon centers in a grid function hex_spacing(size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION return vec2(hex_horizontal_spacing(size, orientation), hex_vertical_spacing(size, orientation)) end @@ -137,19 +150,19 @@ end -- Hex to Screen -- Orientation Must be Either POINTY or FLAT function hex_to_pixel(hex, size, orientation) - local M = orientation and orientation.M or DEFAULT_ORIENTATION.M + local M = orientation and orientation.M or HEX_DEFAULT_ORIENTATION.M - local x = (M[1][1] * hex[1] + M[1][2] * hex[2]) * (size and size[1] or DEFAULT_HEX_SIZE[1]) - local y = (M[2][1] * hex[1] + M[2][2] * hex[2]) * (size and size[2] or DEFAULT_HEX_SIZE[2]) + local x = (M[1][1] * hex[1] + M[1][2] * hex[2]) * (size and size[1] or HEX_DEFAULT_SIZE[1]) + local y = (M[2][1] * hex[1] + M[2][2] * hex[2]) * (size and size[2] or HEX_DEFAULT_SIZE[2]) return vec2(x, y) end -- Screen to Hex -- Orientation Must be Either POINTY or FLAT function pixel_to_hex(pix, size, orientation) - local W = orientation and orientation.W or DEFAULT_ORIENTATION.W + local W = orientation and orientation.W or HEX_DEFAULT_ORIENTATION.W - local pix = pix / (size or vec2(DEFAULT_HEX_SIZE)) + local pix = pix / (size or vec2(HEX_DEFAULT_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] @@ -159,14 +172,14 @@ end -- TODO test, learn am.draw function hex_corner_offset(corner, size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION local angle = 2.0 * math.pi * orientation.angle + corner / 6 return vec2(size[1] * math.cos(angle), size[2] * math.sin(angle)) end -- TODO test this thing function hex_corners(hex, size, orientation) - local orientation = orientation or DEFAULT_ORIENTATION + local orientation = orientation or HEX_DEFAULT_ORIENTATION local corners = {} local center = hex_to_pixel(hex, size, orientation) for i = 0, 5 do @@ -220,7 +233,7 @@ end -- MAPS & STORAGE -- Returns Ordered Ring-Shaped Map of |radius| from |center| -function ring_map(center, radius) +function hex_ring_map(center, radius) local map = {} local walk = center + HEX_DIRECTIONS[6] * radius @@ -235,21 +248,21 @@ function ring_map(center, radius) end -- Returns Ordered Spiral Hexagonal Map of |radius| Rings from |center| -function spiral_map(center, radius) +function hex_spiral_map(center, radius) local map = { center } for i = 1, radius do - table.append(map, ring_map(center, i)) + table.append(map, hex_ring_map(center, i)) end return setmetatable(map, {__index={center=center, radius=radius}}) end -local function map_get(map, hex, y) +function hex_map_get(map, hex, y) if y then return map[hex] and map[hex][y] end return map[hex.x] and map[hex.x][hex.y] end -local function map_set(map, hex, y, v) +function hex_map_set(map, hex, y, v) if v then if map[hex] then map[hex][y] = v @@ -268,7 +281,7 @@ local function map_set(map, hex, y, v) end -- Returns Unordered Parallelogram-Shaped Map of |width| and |height| with Simplex Noise -function parallelogram_map(width, height, seed) +function hex_parallelogram_map(width, height, seed) local seed = seed or math.random(width * height) local map = {} @@ -296,14 +309,14 @@ function parallelogram_map(width, height, seed) seed = seed, neighbours = function(hex) return table.filter(hex_neighbours(hex), function(_hex) - return map_get(map, _hex) + return hex_map_get(map, _hex) end) end }}) end -- Returns Unordered Triangular (Equilateral) Map of |size| with Simplex Noise -function triangular_map(size, seed) +function hex_triangular_map(size, seed) local seed = seed or math.random(size * math.cos(size) / 2) local map = {} @@ -330,14 +343,14 @@ function triangular_map(size, seed) seed = seed, neighbours = function(hex) return table.filter(hex_neighbours(hex), function(_hex) - return map_get(map, _hex) + return hex_map_get(map, _hex) end) end }}) end -- Returns Unordered Hexagonal Map of |radius| with Simplex Noise -function hexagonal_map(radius, seed) +function hex_hexagonal_map(radius, seed) local seed = seed or math.random(radius * 2 * math.pi) local map = {} @@ -369,7 +382,7 @@ function hexagonal_map(radius, seed) seed = seed, neighbours = function(hex) return table.filter(hex_neighbours(hex), function(_hex) - return map_get(map, _hex.x, _hex.y) + return hex_map_get(map, _hex.x, _hex.y) end) end }}) @@ -377,37 +390,43 @@ end -- Returns Unordered Rectangular Map of |width| and |height| with Simplex Noise -- @TODO - this doesn't work for pointy orientations -function rectangular_map(width, height, seed) +function hex_rectangular_map(width, height, orientation, seed) + local orientation = orientation or HEX_DEFAULT_ORIENTATION local seed = seed or math.random(width * height) local map = {} - for i = 0, width - 1 do - map[i] = {} - for j = 0, height - 1 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) + if orientation == HEX_ORIENTATION.FLAT then + for i = 0, width - 1 do + map[i] = {} + for j = 0, height - 1 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 + j = j - math.floor(i/2) -- this is what makes it rectangular + + map[i][j] = noise end - j = j - math.floor(i/2) -- this is what makes it rectangular - - map[i][j] = noise end + elseif orientation == HEX_ORIENTATION.POINTY then + error("don't use this, it's broken") end + return setmetatable(map, { __index = { width = width, height = height, seed = seed, neighbours = function(hex) return table.filter(hex_neighbours(hex), function(_hex) - return map_get(map, _hex) + return hex_map_get(map, _hex) end) end }}) @@ -415,25 +434,30 @@ end --============================================================================ -- PATHFINDING - - -function breadth_first(map, start) +-- note: +-- i kinda feel like after implementing these and making the game, there are tons of reasons +-- why you might want to specialize pathfinding, like you would any other kind of algorithm +-- +-- so, while (in theory) these algorithms work with the maps in this file, your maps and game +-- will have lots of other data which you may want your pathfinding algorithms to care about in some way, +-- that these don't. +-- +function hex_breadth_first(map, start) local frontier = {} frontier[1] = start local distance = {} - distance[start.x] = {} - distance[start.x][start.y] = 0 + hex_map_set(distance, start, 0) while not (#frontier == 0) do local current = table.remove(frontier, 1) for _,neighbour in pairs(map.neighbours(current)) do - local d = map_get(distance, neighbour.x, neighbour.y) + local d = hex_map_get(distance, neighbour.x, neighbour.y) if not d then table.insert(frontier, neighbour) - local current_distance = map_get(distance, current.x, current.y) - map_set(distance, neighbour.x, neighbour.y, current_distance + 1) + local current_distance = hex_map_get(distance, current.x, current.y) + hex_map_set(distance, neighbour.x, neighbour.y, current_distance + 1) end end end @@ -441,17 +465,15 @@ function breadth_first(map, start) return distance end -function dijkstra(map, start, goal, cost_f, neighbour_f) +function hex_dijkstra(map, start, goal, cost_f, neighbour_f) local frontier = {} frontier[1] = { hex = start, priority = 0 } local came_from = {} - came_from[start.x] = {} - came_from[start.x][start.y] = false + hex_map_set(came_from, start, false) local cost_so_far = {} - cost_so_far[start.x] = {} - cost_so_far[start.x][start.y] = 0 + hex_map_set(cost_so_far, start, 0) while not (#frontier == 0) do local current = table.remove(frontier, 1) @@ -461,14 +483,14 @@ function dijkstra(map, start, goal, cost_f, neighbour_f) end for _,neighbour in pairs(neighbour_f(map, current.hex)) do - local new_cost = map_get(cost_so_far, current.hex) + cost_f(map, current.hex, neighbour) - local neighbour_cost = map_get(cost_so_far, neighbour) + local new_cost = hex_map_get(cost_so_far, current.hex) + cost_f(map, current.hex, neighbour) + local neighbour_cost = hex_map_get(cost_so_far, neighbour) if not neighbour_cost or (new_cost < neighbour_cost) then - map_set(cost_so_far, neighbour, new_cost) + hex_map_set(cost_so_far, neighbour, new_cost) local priority = new_cost + math.distance(start, neighbour) table.insert(frontier, { hex = neighbour, priority = priority }) - map_set(came_from, neighbour, current) + hex_map_set(came_from, neighbour, current) end end end @@ -476,7 +498,7 @@ function dijkstra(map, start, goal, cost_f, neighbour_f) return came_from end --- generic A* pathfinding +-- A* pathfinding -- -- |heuristic| has the form: -- function(source, target) -- source and target are vec2's @@ -486,20 +508,15 @@ end -- function (from, to) -- from and to are vec2's -- return some numeric value -- --- returns a map that has map[hex.x][hex.y] = { hex = vec2, priority = number }, --- where the hex is the spot it thinks you should go to from the indexed hex, and priority is the cost of that decision, --- as well as 'made_it' a bool that tells you if we were successful in reaching |goal| -function Astar(map, start, goal, heuristic, cost_f) +function hex_Astar(map, start, goal, heuristic, cost_f) local path = {} - path[start.x] = {} - path[start.x][start.y] = false + hex_map_set(path, start, false) local frontier = {} frontier[1] = { hex = start, priority = 0 } local path_so_far = {} - path_so_far[start.x] = {} - path_so_far[start.x][start.y] = 0 + hex_map_set(path_so_far, start, 0) local made_it = false while not (#frontier == 0) do @@ -511,14 +528,14 @@ function Astar(map, start, goal, heuristic, cost_f) end for _,next_ in pairs(map.neighbours(current.hex)) do - local new_cost = map_get(path_so_far, current.hex.x, current.hex.y) + cost_f(map, current.hex, next_) - local next_cost = map_get(path_so_far, next_.x, next_.y) + local new_cost = hex_map_get(path_so_far, current.hex.x, current.hex.y) + cost_f(map, current.hex, next_) + local next_cost = hex_map_get(path_so_far, next_.x, next_.y) if not next_cost or new_cost < next_cost then - map_set(path_so_far, next_.x, next_.y, new_cost) + hex_map_set(path_so_far, next_.x, next_.y, new_cost) local priority = new_cost + heuristic(goal, next_) table.insert(frontier, { hex = next_, priority = priority }) - map_set(path, next_.x, next_.y, current) + hex_map_set(path, next_.x, next_.y, current) end end end diff --git a/src/mob.lua b/src/mob.lua index b0aa007..986cf5f 100644 --- a/src/mob.lua +++ b/src/mob.lua @@ -7,7 +7,7 @@ MOB_TYPE = { SPOODER = 2 } -MAX_MOB_SIZE = hex_height(HEX_SIZE, ORIENTATION.FLAT) / 2 +MAX_MOB_SIZE = hex_height(HEX_SIZE, HEX_ORIENTATION.FLAT) / 2 MOB_SIZE = MAX_MOB_SIZE MOB_SPECS = { @@ -63,7 +63,7 @@ end -- check if a the tile at |hex| is passable by |mob| function mob_can_pass_through(mob, hex) - local tile = map_get(state.map, hex) + local tile = hex_map_get(state.map, hex) return tile_is_medium_elevation(tile) end @@ -158,7 +158,7 @@ local function resolve_frame_target_for_mob(mob, mob_index) local frame_target, tile = false, false if mob.path then -- we (should) have an explicitly stored target - local path_entry = map_get(mob.path, mob.hex) + local path_entry = hex_map_get(mob.path, mob.hex) if not path_entry then -- we should be just about to reach the target, delete the path. @@ -180,12 +180,12 @@ local function resolve_frame_target_for_mob(mob, mob_index) if #neighbours > 0 then local first_neighbour = neighbours[1] - tile = map_get(state.map, first_neighbour) + tile = hex_map_get(state.map, first_neighbour) local lowest_cost_hex = first_neighbour local lowest_cost = tile.priority or 0 for _,n in pairs(neighbours) do - tile = map_get(state.map, n) + tile = hex_map_get(state.map, n) if not tile.priority then -- if there's no stored priority, that should mean it's the center tile @@ -247,8 +247,8 @@ local function update_mob_beeper(mob, mob_index) -- or between when we last calculated this target and now -- check for that now if mob_can_pass_through(mob, mob.frame_target) then - local from = map_get(state.map, mob.hex) - local to = map_get(state.map, mob.frame_target) + local from = hex_map_get(state.map, mob.hex) + local to = hex_map_get(state.map, mob.frame_target) local rate = (4 * mob.speed - math.abs(to.elevation - from.elevation)) * am.delta_time mob.position = mob.position + math.normalize(hex_to_pixel(mob.frame_target, vec2(HEX_SIZE)) - mob.position) * rate diff --git a/src/projectile.lua b/src/projectile.lua index b716b95..0b01dc1 100644 --- a/src/projectile.lua +++ b/src/projectile.lua @@ -85,7 +85,7 @@ local function update_projectile_shell(projectile, projectile_index) -- right now, it's just the hex we're on and all of its neighbours. -- this is done to avoid having to check every mob on screen, though maybe it's not necessary. local do_explode = false - local search_hexes = spiral_map(projectile.hex, 1) + local search_hexes = hex_spiral_map(projectile.hex, 1) local mobs = {} for _,hex in pairs(search_hexes) do for index,mob in pairs(mobs_on_hex(hex)) do @@ -103,7 +103,7 @@ local function update_projectile_shell(projectile, projectile_index) end end - local tile = map_get(state.map, projectile.hex) + local tile = hex_map_get(state.map, projectile.hex) if tile and tile.elevation >= projectile.props.z then --do_explode = true @@ -143,7 +143,7 @@ local function update_projectile_laser(projectile, projectile_index) -- get a list of hexes that could have something we could hit on them -- right now, it's just the hex we're on and all of its neighbours. -- this is done to avoid having to check every mob on screen, though maybe it's not necessary. - local search_hexes = spiral_map(projectile.hex, 1) + local search_hexes = hex_spiral_map(projectile.hex, 1) local hit_mob_count = 0 local hit_mobs = {} for _,hex in pairs(search_hexes) do diff --git a/src/tower.lua b/src/tower.lua index 506c662..f55247c 100644 --- a/src/tower.lua +++ b/src/tower.lua @@ -131,9 +131,9 @@ function make_tower_node(tower_type) elseif tower_type == TOWER_TYPE.HOWITZER then return am.group{ - am.circle(vec2(0), HEX_SIZE, COLORS.VERY_DARK_GRAY, 6), + am.circle(vec2(0), HEX_SIZE, COLORS.VERY_DARK_GRAY{a=0.8}, 6), am.rotate(state.time or 0) ^ am.group{ - pack_texture_into_sprite(TEXTURES.CANNON1, HEX_PIXEL_HEIGHT, HEX_PIXEL_WIDTH*2) -- CHONK + pack_texture_into_sprite(TEXTURES.CANNON1, HEX_PIXEL_HEIGHT*2, HEX_PIXEL_WIDTH*3) -- CHONK } } elseif tower_type == TOWER_TYPE.LIGHTHOUSE then @@ -276,11 +276,11 @@ function tower_type_is_buildable_on(hex, tile, tower_type) local has_mountain = false local has_ground = false - for _,h in pairs(spiral_map(hex, get_tower_size(tower_type))) do + for _,h in pairs(hex_spiral_map(hex, get_tower_size(tower_type))) do table.merge(blocking_towers, towers_on_hex(h)) table.merge(blocking_mobs, mobs_on_hex(h)) - local tile = map_get(state.map, h) + local tile = hex_map_get(state.map, h) -- this should always be true, unless it is possible to place a tower -- where part of the tower overflows the edge of the map if tile then @@ -332,7 +332,7 @@ function tower_type_is_buildable_on(hex, tile, tower_type) elseif tower_type == TOWER_TYPE.LIGHTHOUSE then local has_water_neighbour = false for _,h in pairs(hex_neighbours(hex)) do - local tile = map_get(state.map, h) + local tile = hex_map_get(state.map, h) if tile and tile.elevation < -0.5 then has_water_neighbour = true @@ -442,7 +442,7 @@ function update_tower_lighthouse(tower, tower_index) -- is within some angle range...? if the mob is heading directly away from the tower, then -- the lighthouse shouldn't do much - local path, made_it = Astar(state.map, tower.hex, m.hex, grid_heuristic, grid_cost) + local path, made_it = hex_Astar(state.map, tower.hex, m.hex, grid_heuristic, grid_cost) if made_it then m.path = path @@ -485,17 +485,20 @@ function make_and_register_tower(hex, tower_type) if tower.size == 0 then tower.hexes = { tower.hex } else - tower.hexes = spiral_map(tower.hex, tower.size) + tower.hexes = hex_spiral_map(tower.hex, tower.size) end tower.height = spec.height for _,h in pairs(tower.hexes) do - local tile = map_get(state.map, h.x, h.y) + local tile = hex_map_get(state.map, h.x, h.y) tile.elevation = tile.elevation + tower.height end if tower.type == TOWER_TYPE.HOWITZER then tower.props.z = tower.height + + elseif tower.type == TOWER_TYPE.LIGHTHOUSE then + tower.perimeter = hex_ring_map(tower.hex, tower.range) end register_entity(state.towers, tower) diff --git a/texture.lua b/texture.lua index f3bae30..c22c8dd 100644 --- a/texture.lua +++ b/texture.lua @@ -13,6 +13,7 @@ end TEXTURES = { LOGO = load_texture("res/logo.png"), GEM1 = load_texture("res/gem1.png"), + SHADED_HEX = load_texture("res/shaded_hex1.png"), -- gui stuff BUTTON1 = load_texture("res/button1.png"),