diff --git a/.DS_Store b/.DS_Store index fb090b4..1572c51 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/NOTES.md b/NOTES.md index 06a58e1..3bb9b44 100644 --- a/NOTES.md +++ b/NOTES.md @@ -10,14 +10,16 @@ MAP RESOURCES towers: -1 - redeye +- redeye long-range laser tower only buildable on mountains and upgraded walls - upgrades: - ??? +- lighthouse + light-emitting static tower + only buildable on tiles adjacent to water + mobs strongly prefer to path around lighthouses --1 - wall +- wall some fraction of the height of the tallest mountain makes mob pathing more difficult @@ -25,7 +27,7 @@ towers: - fortifications - lets you build some qualifying towers on top of this tower - spikes - mobs take damage when climbing --2 - moat +- moat some fraction of the depth of the deepest lake makes mob pathing more difficult diff --git a/main.lua b/main.lua index 00f2ab7..8f0255e 100644 --- a/main.lua +++ b/main.lua @@ -36,10 +36,11 @@ PERF_STATS = false -- result of am.perf_stats() -- should be calle WORLD = false -- root scene node of everything considered to be in the game world -- aka non gui stuff -TIME = 0 -- runtime of the current game in seconds (not whole program runtime) -SCORE = 0 -- score of the player -MONEY = 0 -- available resources -MOUSE = false -- position of the mouse at the start of every frame, if an action is tracking it +TIME = 0 -- runtime of the current game in seconds (not whole program runtime) +SCORE = 0 -- score of the player +STARTING_MONEY = 50 +MONEY = STARTING_MONEY -- available resources +MOUSE = false -- position of the mouse at the start of every frame, if an action is tracking it -- global audio settings MUSIC_VOLUME = 0.1 @@ -68,7 +69,7 @@ local function select_hex(hex) end local function can_do_build(hex, tile, tower_type) - return tower_is_buildable_on(hex, tile, SELECTED_TOWER_TYPE) + return can_afford_tower(MONEY, tower_type) and tower_is_buildable_on(hex, tile, tower_type) end local function game_action(scene) @@ -105,7 +106,14 @@ local function game_action(scene) end if WIN:key_pressed"escape" then - game_end() + WIN.scene"game".paused = true + WIN.scene:action(function() + if WIN:key_pressed"escape" then + WIN.scene"game".paused = false + return true + end + end) + --game_end() elseif WIN:key_pressed"f1" then TRDT = (TRDT + 1) % #table.keys(TRDTS) @@ -171,26 +179,30 @@ local function game_action(scene) end function do_day_night_cycle() - local tstep = (math.sin(TIME) / PERF_STATS.avg_fps + 1)/8 - WORLD"negative_mask".color = vec4(tstep) + local slow = 100 + local tstep = (math.sin(TIME / 100) + 1) / PERF_STATS.avg_fps + WORLD"negative_mask".color = vec4(tstep){a=1} end function game_end() - -- de-initialize stuff delete_all_entities() TIME = 0 SCORE = 0 - MONEY = 0 + MONEY = STARTING_MONEY WORLD = false - WIN.scene = am.scale(1) ^ game_scene() + WIN.scene = am.group(am.scale(1) ^ game_scene()) end function update_score(diff) SCORE = SCORE + diff end +function update_money(diff) + MONEY = MONEY + diff +end + local function toolbelt() local toolbelt_height = hex_height(HEX_SIZE) * 2 local tower_tooltip = am.translate(WIN.left + 10, WIN.bottom + toolbelt_height + 20) @@ -283,6 +295,7 @@ function game_scene() WIN.scene:remove(curtain) end)) + -- 2227 HEX_MAP, WORLD = random_map() local scene = am.group{ @@ -293,7 +306,7 @@ function game_scene() score, money, coords, - } + }:tag"game" scene:action(game_action) --scene:action(am.play(SOUNDS.TRACK1)) @@ -302,6 +315,6 @@ function game_scene() end load_textures() -WIN.scene = am.scale(vec2(1)) ^ game_scene() +WIN.scene = am.group(am.scale(vec2(1)) ^ game_scene()) noglobals() diff --git a/res/arrow.png b/res/arrow.png deleted file mode 100644 index 0073c43..0000000 Binary files a/res/arrow.png and /dev/null differ diff --git a/res/077-8332_001.jpg b/res/cells.jpg similarity index 100% rename from res/077-8332_001.jpg rename to res/cells.jpg diff --git a/res/marquis.png b/res/marquis.png deleted file mode 100644 index 57880d6..0000000 Binary files a/res/marquis.png and /dev/null differ diff --git a/res/mob1_1.png b/res/mob_beeper.png similarity index 100% rename from res/mob1_1.png rename to res/mob_beeper.png diff --git a/res/radar.aseprite b/res/radar.aseprite deleted file mode 100644 index 3c87221..0000000 Binary files a/res/radar.aseprite and /dev/null differ diff --git a/res/tab_icon.zip b/res/tab_icon.zip deleted file mode 100644 index 4169fc7..0000000 Binary files a/res/tab_icon.zip and /dev/null differ diff --git a/res/tab_icon/Tab-02.eps b/res/tab_icon/Tab-02.eps deleted file mode 100644 index 772c3a5..0000000 Binary files a/res/tab_icon/Tab-02.eps and /dev/null differ diff --git a/res/tab_icon/Tab-02.jpg b/res/tab_icon/Tab-02.jpg deleted file mode 100644 index 2eb1b4a..0000000 Binary files a/res/tab_icon/Tab-02.jpg and /dev/null differ diff --git a/res/tab_icon/Tab-02.png b/res/tab_icon/Tab-02.png deleted file mode 100644 index 6849880..0000000 Binary files a/res/tab_icon/Tab-02.png and /dev/null differ diff --git a/res/tab_icon/Tab-02.svg b/res/tab_icon/Tab-02.svg deleted file mode 100644 index 7373085..0000000 --- a/res/tab_icon/Tab-02.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/res/tower1.png b/res/tower1.png deleted file mode 100644 index 10782a8..0000000 Binary files a/res/tower1.png and /dev/null differ diff --git a/res/tower2.png b/res/tower2.png deleted file mode 100644 index ec94374..0000000 Binary files a/res/tower2.png and /dev/null differ diff --git a/res/tower_lighthouse.png b/res/tower_lighthouse.png new file mode 100644 index 0000000..d5cc07c Binary files /dev/null and b/res/tower_lighthouse.png differ diff --git a/res/moat1.png b/res/tower_moat.png similarity index 100% rename from res/moat1.png rename to res/tower_moat.png diff --git a/res/tower_redeye.png b/res/tower_redeye.png new file mode 100644 index 0000000..255337e Binary files /dev/null and b/res/tower_redeye.png differ diff --git a/res/tower_wall.aseprite b/res/tower_wall.aseprite new file mode 100644 index 0000000..2bc3bb1 Binary files /dev/null and b/res/tower_wall.aseprite differ diff --git a/res/tower_wall.png b/res/tower_wall.png new file mode 100644 index 0000000..e43af90 Binary files /dev/null and b/res/tower_wall.png differ diff --git a/res/wall_closed.png b/res/wall_closed.png deleted file mode 100644 index 99ee0e0..0000000 Binary files a/res/wall_closed.png and /dev/null differ diff --git a/res/wall_open.png b/res/wall_open.png deleted file mode 100644 index df42421..0000000 Binary files a/res/wall_open.png and /dev/null differ diff --git a/sound.lua b/sound.lua index 1c0819a..7cbba6a 100644 --- a/sound.lua +++ b/sound.lua @@ -23,12 +23,13 @@ SOUNDS = { TRACK1 = am.track(am.load_audio("res/track1.ogg"), true, 1, 0.1) } --- play a sound with variable pitch -function vplay_sound(seed, range) - return am.play(am.sfxr_synth(seed), false, (math.random() + 0.5)/(range and 1/range or 2)) +-- play sound effect with variable pitch +function vplay_sfx(sound, pitch_range) + local pitch = (math.random() + 0.5)/(pitch_range and 1/pitch_range or 2) + WIN.scene:action(am.play(sound, false, pitch, SFX_VOLUME)) end -function play_sound(seed) - return am.play(am.sfxr_synth(seed), false) +function play_sfx(sound) + WIN.scene:action(am.play(sound, false, 1, SFX_VOLUME)) end diff --git a/src/extra.lua b/src/extra.lua index 63043cf..743bb22 100644 --- a/src/extra.lua +++ b/src/extra.lua @@ -24,3 +24,33 @@ function table.find(t, predicate) return nil end +function 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 + diff --git a/src/tower_spec.lua b/src/game.lua similarity index 50% rename from src/tower_spec.lua rename to src/game.lua index 8b13789..139597f 100644 --- a/src/tower_spec.lua +++ b/src/game.lua @@ -1 +1,2 @@ + diff --git a/src/grid.lua b/src/grid.lua index d77a342..26068dd 100644 --- a/src/grid.lua +++ b/src/grid.lua @@ -2,8 +2,10 @@ -- distance from hex centerpoint to any vertex HEX_SIZE = 20 -HEX_PIXEL_SIZE = vec2(hex_width(HEX_SIZE, ORIENTATION.FLAT) - , hex_height(HEX_SIZE, ORIENTATION.FLAT)) + +HEX_PIXEL_WIDTH = hex_width(HEX_SIZE, ORIENTATION.FLAT) +HEX_PIXEL_HEIGHT = hex_height(HEX_SIZE, ORIENTATION.FLAT) +HEX_PIXEL_DIMENSIONS = vec2(HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT) -- with 1920x1080, this is the minimal dimensions to cover the screen (65x33) -- @NOTE added 2 cell padding, because we terraform the very outer edge and it looks ugly @@ -17,6 +19,9 @@ HEX_GRID_CENTER = vec2(math.floor(HEX_GRID_WIDTH/2) , 0) -- math.floor(HEX_GRID_HEIGHT/2)) +HEX_GRID_MINIMUM_ELEVATION = -1 +HEX_GRID_MAXIMUM_ELEVATION = 1 + -- index is hex coordinates [x][y] -- { { elevation, node, etc. } } HEX_MAP = {} @@ -78,8 +83,9 @@ end function grid_cost(map, from, to) local t1, t2 = map.get(from.x, from.y), map.get(to.x, to.y) - return 1 + 10 * math.abs(math.abs(t1.elevation)^0.5 - - math.abs(t2.elevation)^0.5) + local epsilon = HEX_GRID_MAXIMUM_ELEVATION - HEX_GRID_MINIMUM_ELEVATION + return epsilon + 10 * math.abs(math.abs(t1.elevation)^0.5 + - math.abs(t2.elevation)^0.5) end function generate_and_apply_flow_field(map, start, world) @@ -94,7 +100,7 @@ function generate_and_apply_flow_field(map, start, world) ^ am.text(string.format("%.1f", f.priority * 10))) else -- should fire exactly once per goal hex - log('no priority') + --log('no priority') end end end @@ -135,14 +141,14 @@ function random_map(seed) for j,noise in pairs(map[i]) do local evenq = hex_to_evenq(vec2(i, j)) - -- check if we're on an edge -- terraform edges to be passable if evenq.x == 0 or evenq.x == (HEX_GRID_WIDTH - 1) or -evenq.y == 0 or -evenq.y == (HEX_GRID_HEIGHT - 1) then + -- if we're on an edge -- terraform edges to be passable noise = 0 - -- also terraform the center of the grid to be passable - -- very infrequently, but still sometimes it is not medium elevation elseif i == HEX_GRID_CENTER.x and j == HEX_GRID_CENTER.y then + -- also terraform the center of the grid to be passable + -- very infrequently, but still sometimes it is not medium elevation noise = 0 else @@ -178,10 +184,10 @@ function random_map(seed) end) end - generate_and_apply_flow_field(map, HEX_GRID_CENTER) + generate_and_apply_flow_field(map, HEX_GRID_CENTER, world) - --world:append(am.translate(hex_to_pixel(HEX_GRID_CENTER)) - -- ^ pack_texture_into_sprite(TEX_SATELLITE, HEX_PIXEL_SIZE.x*2, HEX_PIXEL_SIZE.y*2)) + world:append(am.translate(hex_to_pixel(HEX_GRID_CENTER)) + ^ pack_texture_into_sprite(TEX_SATELLITE, HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT)) return map, am.translate(WORLDSPACE_COORDINATE_OFFSET) ^ world end diff --git a/src/mob.lua b/src/mob.lua index c00bdde..7d154f6 100644 --- a/src/mob.lua +++ b/src/mob.lua @@ -11,7 +11,7 @@ mob(entity) structure: --]] MAX_MOB_SIZE = hex_height(HEX_SIZE, ORIENTATION.FLAT) / 2 -MOB_SIZE = MAX_MOB_SIZE/2 +MOB_SIZE = MAX_MOB_SIZE function mobs_on_hex(hex) local t = {} @@ -37,7 +37,7 @@ function mob_can_pass_through(mob, hex) end function mob_die(mob, mob_index) - WORLD:action(vplay_sound(SOUNDS.EXPLOSION1)) + vplay_sfx(SOUNDS.EXPLOSION1) delete_entity(MOBS, mob_index) end @@ -45,6 +45,7 @@ function do_hit_mob(mob, damage, mob_index) mob.health = mob.health - damage if mob.health <= 0 then update_score(mob.bounty) + update_money(mob.bounty) mob_die(mob, mob_index) end end @@ -79,90 +80,97 @@ local function get_spawn_hex() return evenq_to_hex(vec2(x, -y)) end -local function mob_update(mob, mob_index) +local function update_mob(mob, mob_index) local last_frame_hex = mob.hex mob.hex = pixel_to_hex(mob.position) if mob.hex == HEX_GRID_CENTER then - log('die') update_score(-mob.health) mob_die(mob, mob_index) return true end -- figure out movement - local frame_target, tile = nil, nil - if mob.path then - log('A*') - - -- we have an explicitly stored target - local path_entry = mob.path[mob.hex.x] and mob.path[mob.hex.x][mob.hex.y] - frame_target = path_entry.hex - - -- check if our target is valid, and if it's not we aren't going to move this frame. - -- recalculate our path. - if last_frame_hex ~= mob.hex and not mob_can_pass_through(mob, frame_target) then - log('recalc') - mob.path = get_mob_path(mob, HEX_MAP, mob.hex, HEX_GRID_CENTER) - frame_target = nil - end - else - -- use the map's flow field - gotta find the the best neighbour - local neighbours = HEX_MAP.neighbours(mob.hex) - - if #neighbours > 0 then - local first_neighbour = neighbours[1] - tile = HEX_MAP.get(first_neighbour.x, first_neighbour.y) - local lowest_cost_hex = first_neighbour - local lowest_cost = tile.priority or 0 - - for _,n in pairs(neighbours) do - tile = HEX_MAP.get(n.x, n.y) - local current_cost = tile.priority - - if current_cost and current_cost < lowest_cost then - lowest_cost_hex = n - lowest_cost = current_cost - end + if last_frame_hex ~= mob.hex or not mob.frame_target then + local frame_target, tile = nil, nil + if mob.path then + log('A*') + -- we have an explicitly stored target + local path_entry = mob.path[mob.hex.x] and mob.path[mob.hex.x][mob.hex.y] + mob.frame_target = path_entry.hex + + -- check if our target is valid, and if it's not we aren't going to move this frame. + -- recalculate our path. + if last_frame_hex ~= mob.hex and not mob_can_pass_through(mob, frame_target) then + log('recalc') + mob.path = get_mob_path(mob, HEX_MAP, mob.hex, HEX_GRID_CENTER) + mob.frame_target = nil end - - frame_target = lowest_cost_hex else - log('no neighbours') - log(table.tostring(neighbours) .. mob.hex .. mob.position) + -- use the map's flow field - gotta find the the best neighbour + local neighbours = HEX_MAP.neighbours(mob.hex) + + if #neighbours > 0 then + local first_neighbour = neighbours[1] + tile = HEX_MAP.get(first_neighbour.x, first_neighbour.y) + local lowest_cost_hex = first_neighbour + local lowest_cost = tile.priority or 0 + + for _,n in pairs(neighbours) do + tile = HEX_MAP.get(n.x, n.y) + local current_cost = tile.priority + + if current_cost and current_cost < lowest_cost then + lowest_cost_hex = n + lowest_cost = current_cost + end + end + + mob.frame_target = lowest_cost_hex + else + log('no neighbours') + end end end + if mob.frame_target and mob.frame_target == last_frame_hex then + log('backpedaling') + log(mob.frame_target) + log(mob.hex) + log(last_frame_hex) + --WIN.scene.paused = true + end + -- do movement - if frame_target then + if mob.frame_target then -- this is supposed to achieve frame rate independence, but i have no idea if it actually does - local rate = 1 + mob.speed / PERF_STATS.avg_fps + -- the constant multiplier at the beginning is how many pixels we want a mob with speed 1 to move in one frame + local rate = 4 * mob.speed / PERF_STATS.avg_fps - mob.position = mob.position + math.normalize(hex_to_pixel(frame_target) - mob.position) * rate + mob.position = mob.position + math.normalize(hex_to_pixel(mob.frame_target) - mob.position) * rate mob.node.position2d = mob.position else log('no target') end - --[[ passive animation + -- passive animation if math.random() < 0.01 then mob.node"rotate":action(am.tween(0.3, { angle = mob.node"rotate".angle + math.pi*3 })) else mob.node"rotate".angle = math.wrapf(mob.node"rotate".angle + am.delta_time, math.pi*2) end - --]] end -local function make_and_register_mob() +local function make_and_register_mob(mob_type) local mob = make_basic_entity( get_spawn_hex(), - am.circle(vec2(0), MOB_SIZE, COLORS.SUNRAY), - mob_update + am.rotate(TIME) ^ pack_texture_into_sprite(TEX_MOB_BEEPER, MOB_SIZE, MOB_SIZE), + update_mob ) --mob.path = get_mob_path(mob, HEX_MAP, mob.hex, HEX_GRID_CENTER) mob.health = 10 - mob.speed = 100 + mob.speed = 10 mob.bounty = 5 mob.hurtbox_radius = MOB_SIZE diff --git a/src/projectile.lua b/src/projectile.lua index bba2c12..83b4a69 100644 --- a/src/projectile.lua +++ b/src/projectile.lua @@ -10,7 +10,7 @@ bullet/projectile(entity) structure } --]] -function projectile_update(projectile, projectile_index) +function update_projectile(projectile, projectile_index) projectile.position = projectile.position + projectile.vector * projectile.velocity projectile.node.position2d = projectile.position projectile.hex = pixel_to_hex(projectile.position) @@ -63,13 +63,13 @@ function projectile_update(projectile, projectile_index) -- hit the mob, delete ourselves, affect the world do_hit_mob(closest_mob, projectile.damage, closest_mob_index) delete_entity(PROJECTILES, projectile_index) - WORLD:action(vplay_sound(SOUNDS.HIT1, 0.5)) + vplay_sfx(SOUNDS.HIT1, 0.5) end function make_and_register_projectile(hex, vector, velocity, damage, hitbox_radius) local projectile = make_basic_entity(hex , am.line(vector, vector*hitbox_radius, 3, COLORS.CLARET) - , projectile_update) + , update_projectile) projectile.vector = vector projectile.velocity = velocity projectile.damage = damage diff --git a/src/tower.lua b/src/tower.lua index 5ec31fc..813b73b 100644 --- a/src/tower.lua +++ b/src/tower.lua @@ -11,14 +11,30 @@ tower(entity) structure: TOWER_TYPE = { REDEYE = 1, - WALL = 11, - MOAT = 12, + WALL = 2, + MOAT = 3, } +function get_tower_build_cost(tower_type) + if tower_type == TOWER_TYPE.REDEYE then return 25 + elseif tower_type == TOWER_TYPE.WALL then return 5 + elseif tower_type == TOWER_TYPE.MOAT then return 15 + end +end + +function can_afford_tower(money, tower_type) + local cost = get_tower_build_cost(tower_type) + + if tower_type == TOWER_TYPE.REDEYE then return (money - cost) > 0 + elseif tower_type == TOWER_TYPE.WALL then return (money - cost) > 0 + elseif tower_type == TOWER_TYPE.MOAT then return (money - cost) > 0 + end +end + function get_tower_texture(tower_type) - if tower_type == TOWER_TYPE.REDEYE then return TEX_TOWER2 - elseif tower_type == TOWER_TYPE.WALL then return TEX_WALL_CLOSED - elseif tower_type == TOWER_TYPE.MOAT then return TEX_MOAT1 + if tower_type == TOWER_TYPE.REDEYE then return TEX_TOWER_REDEYE + elseif tower_type == TOWER_TYPE.WALL then return TEX_TOWER_WALL + elseif tower_type == TOWER_TYPE.MOAT then return TEX_TOWER_MOAT end end @@ -35,21 +51,17 @@ local function get_tower_update_function(tower_type) end end -local function make_tower_sprite(tower_type) - local texture = get_tower_texture(tower_type) - if tower_type == TOWER_TYPE.REDEYE then - return pack_texture_into_sprite(texture, HEX_PIXEL_SIZE.x, HEX_PIXEL_SIZE.y) - - elseif tower_type == TOWER_TYPE.WALL then - return pack_texture_into_sprite(TEX_WALL_CLOSED, HEX_PIXEL_SIZE.x + 1, HEX_PIXEL_SIZE.y + 1) - --return am.circle(vec2(0), HEX_SIZE, COLORS.VERY_DARK_GRAY, 6) +function update_wall_texture(hex) + for _,n in pairs(hex_neighbours(hex)) do + local tile = HEX_MAP.get(hex.x, hex.y) - elseif tower_type == TOWER_TYPE.MOAT then - --return pack_texture_into_sprite(TEX_MOAT1, HEX_PIXEL_SIZE.x, HEX_PIXEL_SIZE.y) - return am.circle(vec2(0), HEX_SIZE, COLORS.YALE_BLUE, 6) end end +local function make_tower_sprite(tower_type) + return pack_texture_into_sprite(get_tower_texture(tower_type), HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT) +end + function tower_on_hex(hex) return table.find(TOWERS, function(tower) return tower.hex == hex @@ -99,7 +111,7 @@ function update_tower_redeye(tower, tower_index) ) tower.last_shot_time = TIME - tower.node:action(vplay_sound(SOUNDS.LASER2)) + vplay_sfx(SOUNDS.LASER2) end end end @@ -123,7 +135,8 @@ function make_and_register_tower(hex, tower_type) end function build_tower(hex, tower_type) + update_money(-get_tower_build_cost(tower_type)) make_and_register_tower(hex, tower_type) - WIN.scene:action(am.play(am.sfxr_synth(SOUNDS.EXPLOSION4))) + vplay_sfx(SOUNDS.EXPLOSION4) end diff --git a/src/tower_spec.luaa b/src/tower_spec.luaa deleted file mode 100644 index 5985dd6..0000000 --- a/src/tower_spec.luaa +++ /dev/null @@ -1,12 +0,0 @@ - - -REDEYE = { - - - - - - -} - - diff --git a/texture.lua b/texture.lua index 0c74223..4e46ce4 100644 --- a/texture.lua +++ b/texture.lua @@ -1,23 +1,17 @@ function load_textures() - TEX_MARQUIS = am.texture2d("res/marquis.png") - TEX_BUTTON1 = am.texture2d("res/button1.png") TEX_WIDER_BUTTON1 = am.texture2d("res/wider_button1.png") - TEX_TAB_ICON = am.texture2d("res/tab_icon.png") - TEX_ARROW = am.texture2d("res/arrow.png") - - TEX_RADAR1 = am.texture2d("res/radar.png") TEX_SATELLITE = am.texture2d("res/satelite.png") - TEX_WALL_CLOSED = am.texture2d("res/wall_closed.png") - TEX_MOAT1 = am.texture2d("res/moat1.png") - TEX_TOWER1 = am.texture2d("res/tower1.png") - TEX_TOWER2 = am.texture2d("res/tower2.png") - TEX_MOB1_1 = am.texture2d("res/mob1_1.png") - TEX_MOB2_1 = am.texture2d("res/mob2_1.png") + TEX_TOWER_WALL = am.texture2d("res/tower_wall.png") + TEX_TOWER_MOAT = am.texture2d("res/tower_moat.png") + TEX_TOWER_REDEYE = am.texture2d("res/tower_redeye.png") + TEX_TOWER_LIGHTHOUSE = am.texture2d("res/tower_lighthouse.png") + + TEX_MOB_BEEPER = am.texture2d("res/mob_beeper.png") end function pack_texture_into_sprite(texture, width, height)