- 
					10STYLEGUIDE.md
- 
					6conf.lua
- 
					4curves.lua
- 
					8data/towers.lua
- 
					12lib/color.lua
- 
					100lib/extra.lua
- 
					23lib/geometry.lua
- 
					223lib/gui.lua
- 
					33lib/memory.lua
- 
					76lib/random.lua
- 
					25lib/sound.lua
- 
					105lib/texture.lua
- 
					275main.lua
- 
					BINres/cells.jpg
- 
					0res/img/abouthex.png
- 
					0res/img/bagel.jpg
- 
					0res/img/button1.png
- 
					0res/img/curtain1.png
- 
					0res/img/gear.png
- 
					0res/img/gears.png
- 
					0res/img/gem1.png
- 
					0res/img/loadgamehex.png
- 
					0res/img/logo.png
- 
					0res/img/mainmenuhex.png
- 
					0res/img/mapeditorhex.png
- 
					0res/img/mob_beeper.png
- 
					0res/img/mob_spooder.png
- 
					0res/img/mob_velkooz.png
- 
					0res/img/mob_velkooz0.png
- 
					0res/img/mob_velkooz1.png
- 
					0res/img/mob_velkooz2.png
- 
					0res/img/mob_velkooz3.png
- 
					0res/img/newgamehex.png
- 
					0res/img/quithex.png
- 
					0res/img/savegamehex.png
- 
					0res/img/select_box.png
- 
					0res/img/settingshex.png
- 
					0res/img/shaded_hex.png
- 
					0res/img/slider.png
- 
					0res/img/sound-off.png
- 
					0res/img/sound-on.png
- 
					0res/img/tower_gattler.png
- 
					0res/img/tower_gattler_icon.png
- 
					0res/img/tower_howitzer.png
- 
					0res/img/tower_howitzer_icon.png
- 
					0res/img/tower_lighthouse.png
- 
					0res/img/tower_lighthouse_icon.png
- 
					0res/img/tower_moat.png
- 
					0res/img/tower_moat_icon.png
- 
					0res/img/tower_radar.png
- 
					0res/img/tower_radar_icon.png
- 
					0res/img/tower_redeye.png
- 
					0res/img/tower_redeye_icon.png
- 
					0res/img/tower_wall.png
- 
					0res/img/tower_wall_icon.png
- 
					0res/img/unpausehex.png
- 
					BINres/img/white-texture.png
- 
					0res/img/wider_button1.png
- 
					0res/ogg/main_theme.ogg
- 
					4src/entity.lua
- 
					1src/extra.lua
- 
					32src/game.lua
- 
					116src/grid.lua
- 
					65src/hexyz.lua
- 
					16src/map-editor.lua
- 
					3src/memory.lua
- 
					87src/tower.lua
- 
					84texture.lua
| @ -1,10 +0,0 @@ | |||||
| 
 |  | ||||
| ### |  | ||||
| this is a project written in lua, using the amulet game engine. |  | ||||
| if you don't know lua, or know lua but don't know amulet, read or skim the documentation: |  | ||||
| https://amulet.xyz/doc |  | ||||
| 
 |  | ||||
| amulet does extend lua syntax in some small ways. |  | ||||
| 
 |  | ||||
| @TODO |  | ||||
| 
 |  | ||||
| @ -1,4 +0,0 @@ | |||||
| 
 |  | ||||
| -- nice curve, f(0) = ~1.1, f(100) = ~100, exponential slope roughly inbetween |  | ||||
| -- math.exp((x + 1)/22) / 100 |  | ||||
| -- |  | ||||
| @ -0,0 +1,100 @@ | |||||
|  | 
 | ||||
|  | -- utility functions that don't below elsewhere go here, | ||||
|  | -- especially if they would be at home on the global 'math' or 'table' variables, or are otherwise extensions of standard lua features | ||||
|  | -- try to avoid *too* much amulet specific stuff, but vector types are probably ok. | ||||
|  | 
 | ||||
|  | function fprofile(f, ...) | ||||
|  |     local t1 = am.current_time() | ||||
|  |     local result = { f(...) } | ||||
|  |     local time = am.current_time() - t1 | ||||
|  |     --log("%f", time) | ||||
|  |     return time, unpack(result) | ||||
|  | end | ||||
|  | 
 | ||||
|  | function math.wrapf(float, range) | ||||
|  |     return float - range * math.floor(float / range) | ||||
|  | end | ||||
|  | 
 | ||||
|  | function math.lerp(v1, v2, t) | ||||
|  |     return v1 * t + v2 * (1 - t) | ||||
|  | end | ||||
|  | 
 | ||||
|  | -- don't use this with sparse arrays | ||||
|  | function table.rchoice(t) | ||||
|  |     return t[math.floor(math.random() * #t) + 1] | ||||
|  | end | ||||
|  | 
 | ||||
|  | function table.count(t) | ||||
|  |     local count = 0 | ||||
|  |     for i,v in pairs(t) do | ||||
|  |         if v ~= nil then | ||||
|  |             count = count + 1 | ||||
|  |         end | ||||
|  |     end | ||||
|  |     return count | ||||
|  | end | ||||
|  | 
 | ||||
|  | function table.highest_index(t) | ||||
|  |     local highest = nil | ||||
|  |     for i,v in pairs(t) do | ||||
|  |         if i and not highest then | ||||
|  |             highest = i | ||||
|  |         end | ||||
|  | 
 | ||||
|  |         if i > highest then | ||||
|  |             highest = i | ||||
|  |         end | ||||
|  |     end | ||||
|  |     return highest | ||||
|  | end | ||||
|  | 
 | ||||
|  | function table.find(t, predicate) | ||||
|  |     for i,v in pairs(t) do | ||||
|  |         if predicate(v) then | ||||
|  |             return i,v | ||||
|  |         end | ||||
|  |     end | ||||
|  |     return nil | ||||
|  | end | ||||
|  | 
 | ||||
|  | -- don't use with sparse arrays or hash tables. | ||||
|  | -- only arrays. | ||||
|  | -- mutates the array in place. | ||||
|  | function table.reverse(t) | ||||
|  |     local n = #t | ||||
|  |     for i,v in pairs(t) do | ||||
|  |         t[i], t[n] = t[n], t[i] | ||||
|  |         n = n - 1 | ||||
|  |     end | ||||
|  | end | ||||
|  | 
 | ||||
|  | function table.quicksort(t, low_index, high_index, comparator) | ||||
|  |     local function partition(t, low_index, high_index) | ||||
|  |         local i = low_index - 1 | ||||
|  |         local pivot = t[high_index] | ||||
|  | 
 | ||||
|  |         for j = low_index, high_index - 1 do | ||||
|  |             if comparator(t[j], t[pivot]) <= 0 then | ||||
|  |                 i = i + 1 | ||||
|  |                 t[i], t[j] = t[j], t[i] | ||||
|  |             end | ||||
|  |         end | ||||
|  | 
 | ||||
|  |         t[i + 1], t[high_index] = t[high_index], t[i + 1] | ||||
|  |         return i + 1 | ||||
|  |     end | ||||
|  | 
 | ||||
|  |     if #t == 1 then | ||||
|  |         return t | ||||
|  |     end | ||||
|  | 
 | ||||
|  |     if comparator(t[low_index], t[high_index]) < 0 then | ||||
|  |         local partition_index = partition(t, low_index, high_index) | ||||
|  | 
 | ||||
|  |         quicksort(t, low_index, partition_index - 1, comparator) | ||||
|  |         quicksort(t, partition_index + 1, high_index, comparator) | ||||
|  |     end | ||||
|  | 
 | ||||
|  |     return t | ||||
|  | end | ||||
|  | 
 | ||||
| @ -0,0 +1,23 @@ | |||||
|  | 
 | ||||
|  | function circles_intersect(center1, center2, radius1, radius2) | ||||
|  |     local c1, c2, r1, r2 = center1, center2, radius1, radius2 | ||||
|  |     local d = math.distance(center1, center2) | ||||
|  |     local radii_sum = r1 + r2 | ||||
|  |                                     -- touching | ||||
|  |     if d == radii_sum then          return 1 | ||||
|  | 
 | ||||
|  |                                     -- not touching or intersecting | ||||
|  |     elseif d > radii_sum then       return false | ||||
|  | 
 | ||||
|  |                                     -- intersecting | ||||
|  |     else                            return 2 | ||||
|  |     end | ||||
|  | end | ||||
|  | 
 | ||||
|  | function point_in_rect(point, rect) | ||||
|  |     return point.x > rect.x1 | ||||
|  |        and point.x < rect.x2 | ||||
|  |        and point.y > rect.y1 | ||||
|  |        and point.y < rect.y2 | ||||
|  | end | ||||
|  | 
 | ||||
| @ -0,0 +1,223 @@ | |||||
|  | 
 | ||||
|  | -- text popup in the middle of the screen that dissapates | ||||
|  | function gui_alert(message, color, decay_time) | ||||
|  |     win.scene:append( | ||||
|  |         am.scale(3) ^ am.text(message, color or COLORS.WHITE) | ||||
|  |         :action(coroutine.create(function(self) | ||||
|  |             am.wait(am.tween(self, decay_time or 1, { color = vec4(0) }, am.ease_in_out)) | ||||
|  |             win.scene:remove(self) | ||||
|  |         end)) | ||||
|  |     ) | ||||
|  | end | ||||
|  | 
 | ||||
|  | local function gui_make_backing_rect( | ||||
|  |     position, | ||||
|  |     content_width, | ||||
|  |     content_height, | ||||
|  |     padding | ||||
|  | ) | ||||
|  |     local half_width = content_width/2 | ||||
|  |     local half_height = content_height/2 | ||||
|  | 
 | ||||
|  |     local x1 = position[1] - half_width - padding | ||||
|  |     local y1 = position[2] - half_height - padding | ||||
|  |     local x2 = position[1] + half_width + padding | ||||
|  |     local y2 = position[2] + half_height + padding | ||||
|  | 
 | ||||
|  |     return x1, y1, x2, y2 | ||||
|  | end | ||||
|  | 
 | ||||
|  | -- args { | ||||
|  | --  position vec2 | ||||
|  | --  onclick function | ||||
|  | --  padding number | ||||
|  | -- | ||||
|  | --  min_width number | ||||
|  | --  min_height number | ||||
|  | -- | ||||
|  | --  label string | ||||
|  | --  font { | ||||
|  | --    color vec4 | ||||
|  | --    halign "center" | "left" | "right" | ||||
|  | --    valign "center" | "top" | "bottom" | ||||
|  | --  } | ||||
|  | -- } | ||||
|  | function gui_make_button(args) | ||||
|  |     local args = args or {} | ||||
|  | 
 | ||||
|  |     local position = args.position or vec2(0) | ||||
|  |     local onclick = args.onclick | ||||
|  |     local padding = args.padding or 6 | ||||
|  | 
 | ||||
|  |     local min_width = args.min_width or 0 | ||||
|  |     local min_height = args.min_height or 0 | ||||
|  | 
 | ||||
|  |     local label = args.label or "" | ||||
|  |     local font = args.font or { | ||||
|  |         color = vec4(1), | ||||
|  |         halign = "center", | ||||
|  |         valign = "center" | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     local scene = am.group() | ||||
|  | 
 | ||||
|  |     local text = am.text(args.label or "", font.color, font.halign, font.valign) | ||||
|  |     scene:append(am.translate(args.position) ^ text) | ||||
|  | 
 | ||||
|  |     local content_width = math.max(min_width, text.width) | ||||
|  |     local content_height = math.max(min_height, text.height) | ||||
|  | 
 | ||||
|  |     local x1, y1, x2, y2 = gui_make_backing_rect(position, content_width, content_height, padding) | ||||
|  | 
 | ||||
|  |     local back_rect = am.rect(x1 - padding/2, y1, x2, y2 + padding/2, vec4(0, 0, 0, 1)) | ||||
|  |     local front_rect = am.rect(x1, y1, x2, y2, vec4(0.4)) | ||||
|  |     scene:prepend(front_rect) | ||||
|  |     scene:prepend(back_rect) | ||||
|  | 
 | ||||
|  |     scene:action(function(self) | ||||
|  |         if point_in_rect(win:mouse_position(), back_rect) then | ||||
|  |             if win:mouse_pressed"left" then | ||||
|  |                 front_rect.color = vec4(0.4) | ||||
|  | 
 | ||||
|  |                 if onclick then | ||||
|  |                     onclick() | ||||
|  |                 end | ||||
|  |             else | ||||
|  |                 front_rect.color = vec4(0, 0, 0, 1) | ||||
|  |             end | ||||
|  |         else | ||||
|  |             front_rect.color = vec4(0.4) | ||||
|  |         end | ||||
|  |     end) | ||||
|  | 
 | ||||
|  |     return scene | ||||
|  | end | ||||
|  | 
 | ||||
|  | function gui_textfield( | ||||
|  |     position, | ||||
|  |     dimensions, | ||||
|  |     max, | ||||
|  |     disallowed_chars | ||||
|  | ) | ||||
|  |     local width, height = dimensions.x, dimensions.y | ||||
|  |     local disallowed_chars = disallowed_chars or {} | ||||
|  |     local max = max or 10 | ||||
|  |     local padding = padding or 6 | ||||
|  |     local half_width = width/2 | ||||
|  |     local half_height = height/2 | ||||
|  | 
 | ||||
|  |     local x1 = position[1] - half_width - padding | ||||
|  |     local y1 = position[2] - half_height - padding | ||||
|  |     local x2 = position[1] + half_width + padding | ||||
|  |     local y2 = position[2] + half_height + padding | ||||
|  | 
 | ||||
|  |     local back_rect = am.rect(x1 - padding/2, y1, x2, y2 + padding/2, vec4(0, 0, 0, 1)) | ||||
|  |     local front_rect = am.rect(x1, y1, x2, y2, vec4(0.4)) | ||||
|  | 
 | ||||
|  |     local group = am.group{ | ||||
|  |         back_rect, | ||||
|  |         front_rect, | ||||
|  |         am.translate(-width/2 + padding, 0) ^ am.scale(2) ^ am.text("", vec4(0, 0, 0, 1), "left"), | ||||
|  |         am.translate(-width/2 + padding, -8) ^ am.line(vec2(0, 0), vec2(16, 0), 2, vec4(0, 0, 0, 1)) | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     group:action(function(self) | ||||
|  |         local keys = win:keys_pressed() | ||||
|  |         if #keys == 0 then return end | ||||
|  | 
 | ||||
|  |         local chars = {} | ||||
|  |         local shift = win:key_down("lshift") or win:key_down("rshift") | ||||
|  |         for i,k in pairs(keys) do | ||||
|  |             if k:len() == 1 then -- @HACK alphabetical or digit characters | ||||
|  |                 if string.match(k, "%a") then | ||||
|  |                     if shift then | ||||
|  |                         table.insert(chars, k:upper()) | ||||
|  |                     else | ||||
|  |                         table.insert(chars, k) | ||||
|  |                     end | ||||
|  |                 elseif string.match(k, "%d") then | ||||
|  |                     if shift then | ||||
|  |                         if k == "1" then table.insert(chars, "!") | ||||
|  |                         elseif k == "2" then table.insert(chars, "@") | ||||
|  |                         elseif k == "3" then table.insert(chars, "#") | ||||
|  |                         elseif k == "4" then table.insert(chars, "$") | ||||
|  |                         elseif k == "5" then table.insert(chars, "%") | ||||
|  |                         elseif k == "6" then table.insert(chars, "^") | ||||
|  |                         elseif k == "7" then table.insert(chars, "&") | ||||
|  |                         elseif k == "8" then table.insert(chars, "*") | ||||
|  |                         elseif k == "9" then table.insert(chars, "(") | ||||
|  |                         elseif k == "0" then table.insert(chars, ")") | ||||
|  |                         end | ||||
|  |                     else | ||||
|  |                         table.insert(chars, k) | ||||
|  |                     end | ||||
|  |                 end | ||||
|  |             -- begin non-alphabetical/digit | ||||
|  |             elseif k == "minus" then | ||||
|  |                 if shift then table.insert(chars, "_") | ||||
|  |                 else          table.insert(chars, "-") end | ||||
|  |             elseif k == "equals" then | ||||
|  |                 if shift then table.insert(chars, "=") | ||||
|  |                 else          table.insert(chars, "+") end | ||||
|  |             elseif k == "leftbracket" then | ||||
|  |                 if shift then table.insert(chars, "{") | ||||
|  |                 else          table.insert(chars, "[") end | ||||
|  |             elseif k == "rightbracket" then | ||||
|  |                 if shift then table.insert(chars, "}") | ||||
|  |                 else          table.insert(chars, "]") end | ||||
|  |             elseif k == "backslash" then | ||||
|  |                 if shift then table.insert(chars, "|") | ||||
|  |                 else          table.insert(chars, "\\") end | ||||
|  |             elseif k == "semicolon" then | ||||
|  |                 if shift then table.insert(chars, ":") | ||||
|  |                 else          table.insert(chars, ";") end | ||||
|  |             elseif k == "quote" then | ||||
|  |                 if shift then table.insert(chars, "\"") | ||||
|  |                 else          table.insert(chars, "'") end | ||||
|  |             elseif k == "backquote" then | ||||
|  |                 if shift then table.insert(chars, "~") | ||||
|  |                 else          table.insert(chars, "`") end | ||||
|  |             elseif k == "comma" then | ||||
|  |                 if shift then table.insert(chars, "<") | ||||
|  |                 else          table.insert(chars, ",") end | ||||
|  |             elseif k == "period" then | ||||
|  |                 if shift then table.insert(chars, ">") | ||||
|  |                 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 | ||||
|  |                 -- @TODO | ||||
|  |             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 | ||||
|  |     end) | ||||
|  | 
 | ||||
|  |     return group | ||||
|  | end | ||||
|  | 
 | ||||
|  | function gui_open_modal() | ||||
|  | 
 | ||||
|  | end | ||||
|  | 
 | ||||
| @ -0,0 +1,33 @@ | |||||
|  | 
 | ||||
|  | -- the garbage collector decides when to run its cycles on its own, and this can cause frame spikes in your game. | ||||
|  | -- lua provides some amount of control over its garbage collector. | ||||
|  | -- | ||||
|  | -- by storing the average time it takes for a full gc cycle to run, we can check at the end of a frame if we have enough time | ||||
|  | -- to run it for 'free' | ||||
|  | -- | ||||
|  | -- if you wish, you can call 'collectgarbage("stop")', and then: | ||||
|  | --  at the start of each game frame, call and cache the results of 'am.current_time()' - am.frame_time doesn't seem to work as well | ||||
|  | --  at the end of each game frame, call 'check_if_can_collect_garbage_for_free()' with the cached frame time and a desired minimum fps | ||||
|  | -- | ||||
|  | local garbage_collector_cycle_timing_history = {} | ||||
|  | local garbage_collector_average_cycle_time = 0 | ||||
|  | function run_garbage_collector_cycle() | ||||
|  |     local time, result = fprofile(collectgarbage, "collect") | ||||
|  | 
 | ||||
|  |     table.insert(garbage_collector_cycle_timing_history, time) | ||||
|  |     -- re-calc average gc timing | ||||
|  |     local total = 0 | ||||
|  |     for _,v in pairs(garbage_collector_cycle_timing_history) do | ||||
|  |         total = total + v | ||||
|  |     end | ||||
|  |     garbage_collector_average_cycle_time = total / #garbage_collector_cycle_timing_history | ||||
|  | end | ||||
|  | 
 | ||||
|  | function check_if_can_collect_garbage_for_free(frame_start_time, min_fps) | ||||
|  |     -- often this will be polled at the end of a frame to see if we're running fast or slow, | ||||
|  |     -- and if we have some time to kill before the start of the next frame, we could maybe run gc. | ||||
|  |     if (am.current_time() - frame_start_time) < (1 / (min_fps or 60) + garbage_collector_average_cycle_time) then | ||||
|  |         run_garbage_collector_cycle() | ||||
|  |     end | ||||
|  | end | ||||
|  | 
 | ||||
| @ -0,0 +1,76 @@ | |||||
|  | 
 | ||||
|  | -- seed the random number generator with the current time | ||||
|  | math.randomseed(os.clock()) | ||||
|  | 
 | ||||
|  | -- https://stackoverflow.com/a/20177466/12464892 | ||||
|  | --local A1, A2 = 727595, 798405  -- 5^17=D20*A1+A2 | ||||
|  | --local D20, D40 = 1048576, 1099511627776  -- 2^20, 2^40 | ||||
|  | --local X1, X2 = 0, 1 | ||||
|  | --local function rand() | ||||
|  | --    local U = X2*A2 | ||||
|  | --    local V = (X1*A2 + X2*A1) % D20 | ||||
|  | --    V = (V*D20 + U) % D40 | ||||
|  | --    X1 = math.floor(V/D20) | ||||
|  | --    X2 = V - X1*D20 | ||||
|  | --    return V/D40 | ||||
|  | --end | ||||
|  | -- | ||||
|  | --local SEED_BOUNDS = 2^20 - 1 | ||||
|  | --math.randomseed = function(seed) | ||||
|  | --    local v = math.clamp(math.abs(seed), 0, SEED_BOUNDS) | ||||
|  | --    X1 = v | ||||
|  | --    X2 = v + 1 | ||||
|  | --end | ||||
|  | 
 | ||||
|  | -- to enable allowing the random number generator's state to be restored post-load (game-deserialize), | ||||
|  | -- we count the number of times we call math.random(), and on deserialize, seed the random | ||||
|  | -- number generator, and then discard |count| calls. | ||||
|  | local R = math.random | ||||
|  | RANDOM_CALLS_COUNT = 0 | ||||
|  | local function random(n, m) | ||||
|  |     RANDOM_CALLS_COUNT = RANDOM_CALLS_COUNT + 1 | ||||
|  | 
 | ||||
|  |     if n then | ||||
|  |         if m then | ||||
|  |             return R(n, m) | ||||
|  |         else | ||||
|  |             return R(n) | ||||
|  |         end | ||||
|  |     else | ||||
|  |       return R() | ||||
|  |     end | ||||
|  | end | ||||
|  | 
 | ||||
|  | -- whenever we refer to math.random, actually use the function 'random' above | ||||
|  | math.random = random | ||||
|  | 
 | ||||
|  | function g_octave_noise(x, y, num_octaves, seed) | ||||
|  |     local seed = seed or os.clock() | ||||
|  |     local noise = 0 | ||||
|  | 
 | ||||
|  |     for oct = 1, num_octaves do | ||||
|  |         local f = 1/4^oct | ||||
|  |         local l = 2^oct | ||||
|  |         local pos = vec2(x + seed, y + seed) | ||||
|  |         noise = noise + f * math.simplex(pos * l) | ||||
|  |     end | ||||
|  | 
 | ||||
|  |     return noise | ||||
|  | end | ||||
|  | 
 | ||||
|  | ---- @TODO test, fix | ||||
|  | --function poisson_knuth(lambda) | ||||
|  | --    local e = 2.71828 | ||||
|  | -- | ||||
|  | --    local L = e^-lambda | ||||
|  | --    local k = 0 | ||||
|  | --    local p = 1 | ||||
|  | -- | ||||
|  | --    while p > L do | ||||
|  | --        k = k + 1 | ||||
|  | --        p = p * math.random() | ||||
|  | --    end | ||||
|  | -- | ||||
|  | --    return k - 1 | ||||
|  | --end | ||||
|  | 
 | ||||
| @ -0,0 +1,105 @@ | |||||
|  | 
 | ||||
|  | local IMG_FILE_PREFIX = "res/img/" | ||||
|  | 
 | ||||
|  | local function load_texture(filepath) | ||||
|  |     local path = IMG_FILE_PREFIX .. filepath | ||||
|  |     local status, texture = pcall(am.texture2d, path) | ||||
|  | 
 | ||||
|  |     if status then | ||||
|  |         return texture | ||||
|  |     else | ||||
|  |         log("failed to load texture at path: " .. path) | ||||
|  |         return am.texture2d(IMG_FILE_PREFIX .. "bagel.jpg") | ||||
|  |     end | ||||
|  | end | ||||
|  | 
 | ||||
|  | TEXTURES = { | ||||
|  |     -- note that in amulet, if you prefix paths with './', they fail to be found in the exported data.pak | ||||
|  |     WHITE                   = load_texture("white-texture.png"), | ||||
|  |     LOGO                    = load_texture("logo.png"), | ||||
|  |     GEM1                    = load_texture("gem1.png"), | ||||
|  | 
 | ||||
|  |     SHADED_HEX              = load_texture("shaded_hex.png"), | ||||
|  |     NEW_GAME_HEX            = load_texture("newgamehex.png"), | ||||
|  |     SAVE_GAME_HEX           = load_texture("savegamehex.png"), | ||||
|  |     LOAD_GAME_HEX           = load_texture("loadgamehex.png"), | ||||
|  |     SETTINGS_HEX            = load_texture("settingshex.png"), | ||||
|  |     MAP_EDITOR_HEX          = load_texture("mapeditorhex.png"), | ||||
|  |     ABOUT_HEX               = load_texture("abouthex.png"), | ||||
|  |     QUIT_HEX                = load_texture("quithex.png"), | ||||
|  |     UNPAUSE_HEX             = load_texture("unpausehex.png"), | ||||
|  |     MAIN_MENU_HEX           = load_texture("mainmenuhex.png"), | ||||
|  | 
 | ||||
|  |     CURTAIN                 = load_texture("curtain1.png"), | ||||
|  | 
 | ||||
|  |     SOUND_ON1               = load_texture("sound-on.png"), | ||||
|  |     SOUND_OFF               = load_texture("sound-off.png"), | ||||
|  | 
 | ||||
|  |     -- gui stuff | ||||
|  |     BUTTON1                 = load_texture("button1.png"), | ||||
|  |     WIDER_BUTTON1           = load_texture("wider_button1.png"), | ||||
|  |     GEAR                    = load_texture("gear.png"), | ||||
|  | 
 | ||||
|  |     SELECT_BOX              = load_texture("select_box.png"), | ||||
|  | 
 | ||||
|  |     -- tower stuff | ||||
|  |     TOWER_WALL              = load_texture("tower_wall.png"), | ||||
|  |     TOWER_WALL_ICON         = load_texture("tower_wall_icon.png"), | ||||
|  |     TOWER_GATTLER           = load_texture("tower_gattler.png"), | ||||
|  |     TOWER_GATTLER_ICON      = load_texture("tower_gattler_icon.png"), | ||||
|  |     TOWER_HOWITZER          = load_texture("tower_howitzer.png"), | ||||
|  |     TOWER_HOWITZER_ICON     = load_texture("tower_howitzer_icon.png"), | ||||
|  |     TOWER_REDEYE            = load_texture("tower_redeye.png"), | ||||
|  |     TOWER_REDEYE_ICON       = load_texture("tower_redeye_icon.png"), | ||||
|  |     TOWER_MOAT              = load_texture("tower_moat.png"), | ||||
|  |     TOWER_MOAT_ICON         = load_texture("tower_moat_icon.png"), | ||||
|  |     TOWER_RADAR             = load_texture("tower_radar.png"), | ||||
|  |     TOWER_RADAR_ICON        = load_texture("tower_radar_icon.png"), | ||||
|  |     TOWER_LIGHTHOUSE        = load_texture("tower_lighthouse.png"), | ||||
|  |     TOWER_LIGHTHOUSE_ICON   = load_texture("tower_lighthouse_icon.png"), | ||||
|  | 
 | ||||
|  |     -- mob stuff | ||||
|  |     MOB_BEEPER              = load_texture("mob_beeper.png"), | ||||
|  |     MOB_SPOODER             = load_texture("mob_spooder.png"), | ||||
|  |     MOB_VELKOOZ             = load_texture("mob_velkooz.png"), | ||||
|  |     MOB_VELKOOZ1            = load_texture("mob_velkooz1.png"), | ||||
|  |     MOB_VELKOOZ2            = load_texture("mob_velkooz2.png"), | ||||
|  |     MOB_VELKOOZ3            = load_texture("mob_velkooz3.png"), | ||||
|  | } | ||||
|  | 
 | ||||
|  | function pack_texture_into_sprite( | ||||
|  |     texture, | ||||
|  |     width, | ||||
|  |     height, | ||||
|  |     color, | ||||
|  |     s1, | ||||
|  |     s2, | ||||
|  |     t1, | ||||
|  |     t2 | ||||
|  | ) | ||||
|  |     local width, height = width or texture.width, height or texture.height | ||||
|  | 
 | ||||
|  |     local sprite = am.sprite{ | ||||
|  |         texture = texture, | ||||
|  |         s1 = s1 or 0, s2 = s2 or 1, t1 = t1 or 0, t2 = t2 or 1, | ||||
|  |         x1 = 0, x2 = width, width = width, | ||||
|  |         y1 = 0, y2 = height, height = height | ||||
|  |     } | ||||
|  | 
 | ||||
|  |     if color then sprite.color = color end | ||||
|  | 
 | ||||
|  |     return sprite | ||||
|  | end | ||||
|  | 
 | ||||
|  | function update_sprite(sprite, texture, width, height, s1, t1, s2, t2) | ||||
|  |     local s1, t1, s2, t2 = s1 or 0, t1 or 0, s2 or 1, t2 or 1 | ||||
|  |     local width, height = width or texture.width, height or texture.height | ||||
|  | 
 | ||||
|  |     sprite.source = { | ||||
|  |         texture = texture, | ||||
|  |         s1 = s1, t1 = t1, s2 = s2, t2 = t2, | ||||
|  |         x1 = 0, x2 = width, width = width, | ||||
|  |         y1 = 0, y2 = height, height = height | ||||
|  |     } | ||||
|  | end | ||||
|  | 
 | ||||
| Before Width: 2000 | Height: 2000 | Size: 716 KiB | 
| Before Width: 282 | Height: 290 | Size: 59 KiB After Width: 282 | Height: 290 | Size: 59 KiB | 
| Before Width: 820 | Height: 820 | Size: 165 KiB After Width: 820 | Height: 820 | Size: 165 KiB | 
| Before Width: 128 | Height: 128 | Size: 454 B After Width: 128 | Height: 128 | Size: 454 B | 
| Before Width: 1920 | Height: 1080 | Size: 238 KiB After Width: 1920 | Height: 1080 | Size: 238 KiB | 
| Before Width: 200 | Height: 200 | Size: 4.8 KiB After Width: 200 | Height: 200 | Size: 4.8 KiB | 
| Before Width: 980 | Height: 798 | Size: 62 KiB After Width: 980 | Height: 798 | Size: 62 KiB | 
| Before Width: 419 | Height: 483 | Size: 99 KiB After Width: 419 | Height: 483 | Size: 99 KiB | 
| Before Width: 282 | Height: 290 | Size: 50 KiB After Width: 282 | Height: 290 | Size: 50 KiB | 
| Before Width: 905 | Height: 483 | Size: 138 KiB After Width: 905 | Height: 483 | Size: 138 KiB | 
| Before Width: 282 | Height: 290 | Size: 59 KiB After Width: 282 | Height: 290 | Size: 59 KiB | 
| Before Width: 283 | Height: 290 | Size: 58 KiB After Width: 283 | Height: 290 | Size: 58 KiB | 
| Before Width: 638 | Height: 639 | Size: 6.1 KiB After Width: 638 | Height: 639 | Size: 6.1 KiB | 
| Before Width: 40 | Height: 40 | Size: 558 B After Width: 40 | Height: 40 | Size: 558 B | 
| Before Width: 450 | Height: 392 | Size: 26 KiB After Width: 450 | Height: 392 | Size: 26 KiB | 
| Before Width: 64 | Height: 64 | Size: 2.7 KiB After Width: 64 | Height: 64 | Size: 2.7 KiB | 
| Before Width: 64 | Height: 64 | Size: 1.9 KiB After Width: 64 | Height: 64 | Size: 1.9 KiB | 
| Before Width: 64 | Height: 64 | Size: 1.9 KiB After Width: 64 | Height: 64 | Size: 1.9 KiB | 
| Before Width: 64 | Height: 64 | Size: 1.8 KiB After Width: 64 | Height: 64 | Size: 1.8 KiB | 
| Before Width: 282 | Height: 290 | Size: 59 KiB After Width: 282 | Height: 290 | Size: 59 KiB | 
| Before Width: 282 | Height: 290 | Size: 57 KiB After Width: 282 | Height: 290 | Size: 57 KiB | 
| Before Width: 283 | Height: 290 | Size: 58 KiB After Width: 283 | Height: 290 | Size: 58 KiB | 
| Before Width: 128 | Height: 128 | Size: 404 B After Width: 128 | Height: 128 | Size: 404 B | 
| Before Width: 282 | Height: 290 | Size: 57 KiB After Width: 282 | Height: 290 | Size: 57 KiB | 
| Before Width: 253 | Height: 290 | Size: 49 KiB After Width: 253 | Height: 290 | Size: 49 KiB | 
| Before Width: 400 | Height: 20 | Size: 442 B After Width: 400 | Height: 20 | Size: 442 B | 
| Before Width: 980 | Height: 728 | Size: 28 KiB After Width: 980 | Height: 728 | Size: 28 KiB | 
| Before Width: 835 | Height: 653 | Size: 123 KiB After Width: 835 | Height: 653 | Size: 123 KiB | 
| Before Width: 64 | Height: 64 | Size: 335 B After Width: 64 | Height: 64 | Size: 335 B | 
| Before Width: 64 | Height: 64 | Size: 360 B After Width: 64 | Height: 64 | Size: 360 B | 
| Before Width: 64 | Height: 64 | Size: 292 B After Width: 64 | Height: 64 | Size: 292 B | 
| Before Width: 64 | Height: 64 | Size: 366 B After Width: 64 | Height: 64 | Size: 366 B | 
| Before Width: 64 | Height: 64 | Size: 796 B After Width: 64 | Height: 64 | Size: 796 B | 
| Before Width: 64 | Height: 64 | Size: 796 B After Width: 64 | Height: 64 | Size: 796 B | 
| Before Width: 137 | Height: 137 | Size: 1.4 KiB After Width: 137 | Height: 137 | Size: 1.4 KiB | 
| Before Width: 137 | Height: 137 | Size: 1.4 KiB After Width: 137 | Height: 137 | Size: 1.4 KiB | 
| Before Width: 300 | Height: 300 | Size: 30 KiB After Width: 300 | Height: 300 | Size: 30 KiB | 
| Before Width: 300 | Height: 300 | Size: 30 KiB After Width: 300 | Height: 300 | Size: 30 KiB | 
| Before Width: 64 | Height: 64 | Size: 637 B After Width: 64 | Height: 64 | Size: 637 B | 
| Before Width: 64 | Height: 64 | Size: 637 B After Width: 64 | Height: 64 | Size: 637 B | 
| Before Width: 126 | Height: 131 | Size: 1.4 KiB After Width: 126 | Height: 131 | Size: 1.4 KiB | 
| Before Width: 126 | Height: 131 | Size: 1.4 KiB After Width: 126 | Height: 131 | Size: 1.4 KiB | 
| Before Width: 282 | Height: 290 | Size: 59 KiB After Width: 282 | Height: 290 | Size: 59 KiB | 
| After Width: 32 | Height: 32 | Size: 117 B | 
| Before Width: 256 | Height: 128 | Size: 533 B After Width: 256 | Height: 128 | Size: 533 B | 
| @ -1,84 +0,0 @@ | |||||
| 
 |  | ||||
| local fail_count = 0 |  | ||||
| local function load_texture(filepath) |  | ||||
|     local status, texture = pcall(am.texture2d, filepath) |  | ||||
| 
 |  | ||||
|     if status then |  | ||||
|         return texture |  | ||||
|     else |  | ||||
|         fail_count = fail_count + 1 |  | ||||
|         log(filepath) |  | ||||
|         return am.texture2d("res/bagel.jpg") |  | ||||
|     end |  | ||||
| end |  | ||||
| 
 |  | ||||
| TEXTURES = { |  | ||||
|     -- note that in amulet, if you prefix paths with './', they fail to be found in the exported data.pak |  | ||||
|     LOGO                    = load_texture("res/logo.png"), |  | ||||
|     GEM1                    = load_texture("res/gem1.png"), |  | ||||
| 
 |  | ||||
|     SHADED_HEX              = load_texture("res/shaded_hex.png"), |  | ||||
|     NEW_GAME_HEX            = load_texture("res/newgamehex.png"), |  | ||||
|     SAVE_GAME_HEX           = load_texture("res/savegamehex.png"), |  | ||||
|     LOAD_GAME_HEX           = load_texture("res/loadgamehex.png"), |  | ||||
|     SETTINGS_HEX            = load_texture("res/settingshex.png"), |  | ||||
|     MAP_EDITOR_HEX          = load_texture("res/mapeditorhex.png"), |  | ||||
|     ABOUT_HEX               = load_texture("res/abouthex.png"), |  | ||||
|     QUIT_HEX                = load_texture("res/quithex.png"), |  | ||||
|     UNPAUSE_HEX             = load_texture("res/unpausehex.png"), |  | ||||
|     MAIN_MENU_HEX           = load_texture("res/mainmenuhex.png"), |  | ||||
| 
 |  | ||||
|     CURTAIN                 = load_texture("res/curtain1.png"), |  | ||||
| 
 |  | ||||
|     SOUND_ON1               = load_texture("res/sound-on.png"), |  | ||||
|     SOUND_OFF               = load_texture("res/sound-off.png"), |  | ||||
| 
 |  | ||||
|     -- gui stuff |  | ||||
|     BUTTON1                 = load_texture("res/button1.png"), |  | ||||
|     WIDER_BUTTON1           = load_texture("res/wider_button1.png"), |  | ||||
|     GEAR                    = load_texture("res/gear.png"), |  | ||||
| 
 |  | ||||
|     SELECT_BOX              = load_texture("res/select_box.png"), |  | ||||
| 
 |  | ||||
|     -- tower stuff |  | ||||
|     TOWER_WALL              = load_texture("res/tower_wall.png"), |  | ||||
|     TOWER_WALL_ICON         = load_texture("res/tower_wall_icon.png"), |  | ||||
|     TOWER_GATTLER           = load_texture("res/tower_gattler.png"), |  | ||||
|     TOWER_GATTLER_ICON      = load_texture("res/tower_gattler_icon.png"), |  | ||||
|     TOWER_HOWITZER          = load_texture("res/tower_howitzer.png"), |  | ||||
|     TOWER_HOWITZER_ICON     = load_texture("res/tower_howitzer_icon.png"), |  | ||||
|     TOWER_REDEYE            = load_texture("res/tower_redeye.png"), |  | ||||
|     TOWER_REDEYE_ICON       = load_texture("res/tower_redeye_icon.png"), |  | ||||
|     TOWER_MOAT              = load_texture("res/tower_moat.png"), |  | ||||
|     TOWER_MOAT_ICON         = load_texture("res/tower_moat_icon.png"), |  | ||||
|     TOWER_RADAR             = load_texture("res/tower_radar.png"), |  | ||||
|     TOWER_RADAR_ICON        = load_texture("res/tower_radar_icon.png"), |  | ||||
|     TOWER_LIGHTHOUSE        = load_texture("res/tower_lighthouse.png"), |  | ||||
|     TOWER_LIGHTHOUSE_ICON   = load_texture("res/tower_lighthouse_icon.png"), |  | ||||
| 
 |  | ||||
|     -- mob stuff |  | ||||
|     MOB_BEEPER              = load_texture("res/mob_beeper.png"), |  | ||||
|     MOB_SPOODER             = load_texture("res/mob_spooder.png"), |  | ||||
|     MOB_VELKOOZ             = load_texture("res/mob_velkooz.png"), |  | ||||
|     MOB_VELKOOZ1            = load_texture("res/mob_velkooz1.png"), |  | ||||
|     MOB_VELKOOZ2            = load_texture("res/mob_velkooz2.png"), |  | ||||
|     MOB_VELKOOZ3            = load_texture("res/mob_velkooz3.png"), |  | ||||
| } |  | ||||
| 
 |  | ||||
| function pack_texture_into_sprite(texture, width, height, color) |  | ||||
|     local sprite = am.sprite{ |  | ||||
|         texture = texture, |  | ||||
|         s1 = 0, s2 = 1, t1 = 0, t2 = 1, |  | ||||
|         x1 = 0, x2 = width, width = width, |  | ||||
|         y1 = 0, y2 = height, height = height |  | ||||
|     } |  | ||||
| 
 |  | ||||
|     if color then sprite.color = color end |  | ||||
| 
 |  | ||||
|     return sprite |  | ||||
| end |  | ||||
| 
 |  | ||||
| if fail_count > 0 then |  | ||||
|     log("failed to load %d texture(s)", fail_count) |  | ||||
| end |  | ||||
| 
 |  | ||||