Nicholas Hayashi 4 years ago
parent
commit
7edfeb6e33
  1. 4
      curves.lua
  2. 82
      main.lua
  3. BIN
      res/curtain1.png
  4. BIN
      res/gear.png
  5. BIN
      res/gears.png
  6. BIN
      res/maintheme.ogg
  7. BIN
      res/select_box.png
  8. BIN
      res/track1.ogg
  9. BIN
      res/unpausehex.png
  10. 6
      sound.lua
  11. 101
      src/game.lua
  12. 51
      src/grid.lua
  13. 16
      src/hexyz.lua
  14. 4
      src/tower.lua
  15. 10
      texture.lua

4
curves.lua

@ -0,0 +1,4 @@
-- nice curve, f(0) = ~1.1, f(100) = ~100, exponential slope roughly inbetween
-- math.exp((x + 1)/22) / 100
--

82
main.lua

@ -1,8 +1,6 @@
-- @TODO
-- music
-- -- title theme
-- -- game theme
--
-- settings menu
-- -- music volume
@ -21,21 +19,39 @@
-- -- move home?
--
-- game
-- -- is 1920x1080 the optimal default resolution?
-- -- HEX_GRID_CENTER =/= HOME
-- -- allow selecting of tiles, if tower is selected then allow sell/upgrade
-- -- button/ui to open pause menu - make it obvious that 'esc' is the button
-- -- play the game and tweak numbers
-- -- new game menu allowing set seed
-- -- gattling gun tower
-- -- gattling gun tower (fast fire rate)
-- -- spooder mob
-- -- make art, birds-eye-ify the redeye tower and lighthouse maybe?
-- resolutions and aspect ratios seem like a huge mess
-- for now, i think we should enforce 4:3
local RESOLUTION_OPTIONS = {
-- 16:9
-- { width = 1776, height = 1000 },
-- { width = 1920, height = 1080 },
-- { width = 1600, height = 900 },
-- 4:3
{ width = 1440, height = 1080 },
{ width = 1400, height = 1050 }, -- seems like a good one
{ width = 1280, height = 960 },
{ width = 1152, height = 864 },
{ width = 1024, height = 768 },
{ width = 960, height = 720 },
{ width = 832, height = 624 },
{ width = 800, height = 600 },
}
settings = am.load_state("settings", "json") or {
fullscreen = false,
window_width = 1920,
window_height = 1080,
music_volume = 0.1,
window_width = 1400,
window_height = 1050,
music_volume = 0.2,
sfx_volume = 0.1,
}
@ -51,9 +67,11 @@ do
height = settings.window_height,
title = "hexyz",
mode = settings.fullscreen and "fullscreen" or "windowed",
resizable = true,
highdpi = true,
letterbox = true,
resizable = true, -- user should probably set their resolution instead of resizing the window, but hey.
show_cursor = true,
}
end
@ -75,22 +93,26 @@ require "src/projectile"
require "src/tower"
-- js style popup in the middle of the screen that dissapates
function alert(message)
-- text popup in the middle of the screen that dissapates, call from anywhere
function alert(message, color)
win.scene:append(
am.scale(3) ^ am.text(message)
am.scale(3) ^ am.text(message, color or COLORS.WHITE)
:action(coroutine.create(function(self)
am.wait(am.tween(self, 1, { color = vec4(0) }))
am.wait(am.tween(self, 1, { color = vec4(0) }, am.ease_out))
win.scene:remove(self)
end))
)
end
function unpause(root_node)
win.scene("game").paused = false
win.scene:remove(root_node)
end
function main_action(self)
if win:key_pressed("escape") then
if win.scene("game") then
win.scene("game").paused = false
win.scene:remove(self)
if game then
unpause(self)
else
--win:close()
end
@ -102,9 +124,8 @@ end
function make_main_scene_toolbelt()
local include_save_option = game
local include_unpause_option = game
local options = {
false,
false,
false,
{
texture = TEXTURES.NEW_GAME_HEX,
@ -128,38 +149,36 @@ function make_main_scene_toolbelt()
local save = am.load_state("save", "json")
if save then
win.scene:remove"menu"
win.scene:remove("menu")
game_init(save)
else
alert("no saved games")
end
end
},
false,
{
texture = TEXTURES.MAP_EDITOR_HEX,
action = function() alert("not yet :)") end
},
include_unpause_option and {
texture = TEXTURES.UNPAUSE_HEX,
action = function() unpause(win.scene("menu")) end
} or false,
{
texture = TEXTURES.SETTINGS_HEX,
action = function() alert("not yet :)") end
},
{
texture = TEXTURES.ABOUT_HEX,
action = function() alert("not yet :)") end
},
false,
{
texture = TEXTURES.QUIT_HEX,
action = function() win:close() end
}
},
false
}
local spacing = 160
local spacing = 150
-- calculate the dimensions of the whole grid
local grid_width = 8
local grid_width = 6
local grid_height = 2
local hhs = hex_horizontal_spacing(spacing)
local hvs = hex_vertical_spacing(spacing)
@ -240,9 +259,12 @@ function main_scene(do_backdrop, do_logo)
end
group:append(hex_backdrop)
else
group:append(am.rect(win.left, win.bottom, win.right, win.top, COLORS.TRANSPARENT))
group:append(
pack_texture_into_sprite(TEXTURES.CURTAIN, win.width, win.height)
)
end
-- @TODO add a hyperlink to an 'about' page or something
group:append(
am.translate(win.right - 10, win.bottom + 10)
^ am.text(string.format("v%s, by %s", version, author), COLORS.WHITE, "right", "bottom")
@ -265,5 +287,7 @@ end
win.scene = am.group(
main_scene(true, true)
)
play_track(SOUNDS.MAIN_THEME)
noglobals()

BIN
res/curtain1.png

After

Width: 1920  |  Height: 1080  |  Size: 238 KiB

BIN
res/gear.png

After

Width: 200  |  Height: 200  |  Size: 4.8 KiB

BIN
res/gears.png

After

Width: 980  |  Height: 798  |  Size: 62 KiB

BIN
res/maintheme.ogg

BIN
res/select_box.png

After

Width: 980  |  Height: 980  |  Size: 30 KiB

BIN
res/track1.ogg

BIN
res/unpausehex.png

After

Width: 282  |  Height: 290  |  Size: 60 KiB

6
sound.lua

@ -22,7 +22,7 @@ SOUNDS = {
RANDOM5 = 36680709,
-- audio buffers
TRACK1 = am.track(am.load_audio("res/track1.ogg"), true, 1, 0.1)
MAIN_THEME = am.track(am.load_audio("res/maintheme.ogg"), true, 1, settings.music_volume)
}
-- play sound effect with variable pitch
@ -35,3 +35,7 @@ function play_sfx(sound)
win.scene:action(am.play(sound, false, 1, settings.sfx_volume))
end
function play_track(track)
win.scene:action(am.play(track))
end

101
src/game.lua

@ -6,9 +6,7 @@ state = {}
function game_end()
state = {}
game = false
-- @TODO
end
function update_score(diff) state.score = state.score + diff end
@ -49,7 +47,7 @@ local function get_initial_game_state(seed)
time_until_next_wave = 0,
time_until_next_break = 0,
spawning = false,
spawn_chance = 0.002,
spawn_chance = 0,
last_mob_spawn_time = 0,
selected_tower_type = false,
@ -216,6 +214,7 @@ local function game_action(scene)
state.current_wave = state.current_wave + 1
state.spawning = false
state.time_until_next_wave = get_break_time(state.current_wave)
end
else
@ -224,10 +223,11 @@ local function game_action(scene)
if state.time_until_next_wave <= 0 then
state.time_until_next_wave = 0
state.spawning = true
-- calculate spawn chance for next wave
state.spawn_chance = (state.current_wave + 1)/200
state.spawn_chance = math.log(state.current_wave) + 0.002
state.spawning = true
state.time_until_next_break = get_wave_time(state.current_wave)
end
end
@ -255,7 +255,7 @@ local function game_action(scene)
node.color = COLORS.CLARET
node:action(am.tween(0.1, { color = COLORS.TRANSPARENT }))
play_sfx(SOUNDS.BIRD2)
alert("breaks flow field")
alert("closes the circle")
elseif cost > state.money then
local node = win.scene("cursor"):child(2)
@ -272,6 +272,9 @@ local function game_action(scene)
apply_flow_field(state.map, flow_field, state.world)
end
end
elseif not state.selected_tower_type then
-- interactable tile, but no tower type selected
end
end
end
@ -371,7 +374,7 @@ local function make_game_toolbelt()
return button
end
local toolbelt_height = win.height * 0.08
local toolbelt_height = win.height * 0.07
local function get_tower_tooltip_text_node(tower_type)
local name = get_tower_name(tower_type)
local placement_rules = get_tower_placement_rules_text(tower_type)
@ -400,48 +403,68 @@ local function make_game_toolbelt()
)
:tag"tower_tooltip_text"
end
local toolbelt = am.group{
local toolbelt = am.group(
am.group():tag"tower_tooltip_text",
am.rect(win.left, win.bottom, win.right, win.bottom + toolbelt_height, COLORS.TRANSPARENT)
}
)
:tag"toolbelt"
local padding = 15
local padding = 12
local size = toolbelt_height - padding
local half_size = size/2
local offset = vec2(win.left + padding*3, win.bottom + padding/3)
local tab_button = am.translate(vec2(0, half_size) + offset) ^ am.group{
pack_texture_into_sprite(TEXTURES.WIDER_BUTTON1, 54, 32),
pack_texture_into_sprite(TEXTURES.TAB_ICON, 25, 25)
}
toolbelt:append(tab_button)
local offset = vec2(win.left, win.bottom + padding/3)
local tower_select_square = (
am.translate(vec2(size + padding, half_size) + offset)
^ am.rect(-size/2-3, -size/2-3, size/2+3, size/2+3, COLORS.SUNRAY)
)
:tag"tower_select_square"
tower_select_square.hidden = true
toolbelt:append(tower_select_square)
local keys = { '1', '2', '3', '4', 'q', 'w', 'e', 'r', 'a', 's', 'd', 'f' }
--local keys = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=' }
-- order of this array is the order of towers on the toolbelt.
local tower_type_values = {
local TOOLBELT_OPTION = {
SELECT = 11
}
local toolbelt_options = {
TOWER_TYPE.WALL,
TOWER_TYPE.HOWITZER,
TOWER_TYPE.REDEYE,
TOWER_TYPE.MOAT,
TOWER_TYPE.RADAR,
TOWER_TYPE.LIGHTHOUSE
TOWER_TYPE.LIGHTHOUSE,
-- reserved for tower types
false,
false,
false,
false,
TOOLBELT_OPTION.SELECT,
false
}
for i,v in pairs(tower_type_values) do
local icon_texture = get_tower_icon_texture(tower_type_values[i])
local tower_type_count = table.count(TOWER_TYPE)
local function get_toolbelt_icon_texture(i)
if i <= tower_type_count then
return get_tower_icon_texture(toolbelt_options[i])
else
local toolbelt_option = TOOLBELT_OPTION[i - tower_type_count]
if toolbelt_option then
if toolbelt_option == TOOLBELT_OPTION.SELECT then
return TEXTURES.SELECT_BOX_ICON
end
end
end
end
for i,v in pairs(toolbelt_options) do
toolbelt:append(
toolbelt_button(
size,
half_size,
icon_texture,
get_toolbelt_icon_texture(i),
padding,
i,
offset,
@ -450,6 +473,30 @@ local function make_game_toolbelt()
)
end
local settings_button_position = vec2(win.right - half_size - 20, win.bottom + half_size + padding/3)
local settings_button_rect = {
x1 = settings_button_position.x - size/2,
y1 = settings_button_position.y - size/2,
x2 = settings_button_position.x + size/2,
y2 = settings_button_position.y + size/2
}
-- make the 'escape/pause/settings' button in the lower right
toolbelt:append(
am.translate(settings_button_position)
^ am.group(
pack_texture_into_sprite(TEXTURES.BUTTON1, size, size),
pack_texture_into_sprite(TEXTURES.GEAR, size-padding, size-padding)
)
:action(function(self)
if point_in_rect(win:mouse_position(), settings_button_rect) then
if win:mouse_pressed("left") then
game_pause()
end
end
end)
)
select_tower_type = function(tower_type)
state.selected_tower_type = tower_type
@ -470,6 +517,7 @@ local function make_game_toolbelt()
else
-- de-selecting currently selected tower if any
toolbelt("tower_select_square").hidden = true
log('hi2')
win.scene:replace("cursor", make_hex_cursor(0, COLORS.TRANSPARENT))
end
@ -478,10 +526,15 @@ local function make_game_toolbelt()
select_toolbelt_button = function(i)
state.selected_toolbelt_button = i
if tower_type_values[i] then
if i <= tower_type_count then
select_tower_type(i)
else
select_tower_type(nil)
if i == 11 then
log('hi')
end
end
end

51
src/grid.lua

@ -1,37 +1,28 @@
-- distance from hex centerpoint to any vertex
HEX_SIZE = 26
HEX_PIXEL_WIDTH = hex_width(HEX_SIZE, HEX_ORIENTATION.FLAT)
HEX_PIXEL_HEIGHT = hex_height(HEX_SIZE, HEX_ORIENTATION.FLAT)
HEX_PIXEL_DIMENSIONS = vec2(HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT)
do
-- add padding, because we terraform the very outer edge and it looks ugly, so hide it
local padding = 3
local padding = 2
HEX_GRID_WIDTH = math.floor(win.width / (HEX_PIXEL_WIDTH + HEX_SIZE) * 2) + padding
HEX_GRID_HEIGHT = math.floor(win.height / HEX_PIXEL_HEIGHT) + padding
-- odd numbers are important because we want a 'true' center
if HEX_GRID_WIDTH % 2 == 0 then
HEX_GRID_WIDTH = HEX_GRID_WIDTH + 1
end
if HEX_GRID_HEIGHT % 2 == 0 then
HEX_GRID_HEIGHT = HEX_GRID_HEIGHT + 1
end
-- the size of the grid should basically always be constant (i think),
-- but different aspect ratios complicate this in an annoying way
HEX_GRID_WIDTH = 41 + padding
HEX_GRID_HEIGHT = 27 + padding
HEX_GRID_DIMENSIONS = vec2(HEX_GRID_WIDTH, HEX_GRID_HEIGHT)
-- leaving y == 0 makes this the center in hex/cube coordinates
-- assuming that our dimensions are correct (odd numbers)
HEX_GRID_CENTER = vec2(math.floor(HEX_GRID_WIDTH/2)
, 0)
HEX_GRID_CENTER = evenq_to_hex(vec2(math.floor(HEX_GRID_WIDTH/2)
, -math.floor(HEX_GRID_HEIGHT/2)))
end
HEX_GRID_MINIMUM_ELEVATION = -1
HEX_GRID_MAXIMUM_ELEVATION = 1
-- pixel distance from hex centerpoint to any vertex
-- given a grid width gx, and window width wx, what's the smallest size a hex can be to fill the whole screen?
-- wx / (gx * 3 / 2)
HEX_SIZE = win.width / (41 * 3 / 2)
HEX_PIXEL_WIDTH = hex_width(HEX_SIZE, HEX_ORIENTATION.FLAT)
HEX_PIXEL_HEIGHT = hex_height(HEX_SIZE, HEX_ORIENTATION.FLAT)
HEX_PIXEL_DIMENSIONS = vec2(HEX_PIXEL_WIDTH, HEX_PIXEL_HEIGHT)
do
local hhs = hex_horizontal_spacing(HEX_SIZE)
@ -63,13 +54,8 @@ function evenq_is_in_interactable_region(evenq)
})
end
function is_water_elevation(elevation)
return elevation < -0.5
end
function is_mountain_elevation(elevation)
return elevation >= 0.5
end
function is_water_elevation(elevation) return elevation < -0.5 end
function is_mountain_elevation(elevation) return elevation >= 0.5 end
function tile_is_medium_elevation(tile)
return tile.elevation >= -0.5 and tile.elevation < 0.5
@ -79,6 +65,8 @@ function grid_heuristic(source, target)
return math.distance(source, target)
end
HEX_GRID_MINIMUM_ELEVATION = -1
HEX_GRID_MAXIMUM_ELEVATION = 1
function grid_cost(map, from, to)
local t1, t2 = hex_map_get(map, from), hex_map_get(map, to)
@ -229,7 +217,6 @@ function make_hex_grid_scene(map)
end
apply_flow_field(map, generate_flow_field(map, HEX_GRID_CENTER), world)
return am.translate(WORLDSPACE_COORDINATE_OFFSET) ^ world
end

16
src/hexyz.lua

@ -9,18 +9,16 @@
-- 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
error("clobbering a math.round function, oopsie!")
end
if not table.append then
end
if not table.filter then
error("clobbering 'math.round', oopsie!")
end
-- @TODO
if not table.append then end
if not table.filter then end
-- wherever 'orientation' appears as an argument, use one of these two, or set a default just below
HEX_ORIENTATION = {
@ -235,6 +233,10 @@ end
--============================================================================
-- MAPS & STORAGE
-- maps that use their indices as the hex coordinates (parallelogram, hexagonal, rectangular, triangular),
-- fail to serialize ideally because they use negative indices, which json doesn't support
-- Returns Ordered Ring-Shaped Map of |radius| from |center|
function hex_ring_map(center, radius)
local map = {}

4
src/tower.lua

@ -264,11 +264,9 @@ function tower_on_hex(hex)
end
function tower_type_is_buildable_on(hex, tile, tower_type)
-- this function gets polled a lot, and sometimes with nil/false tower types
if not tower_type then return false end
-- @TODO remove this shit
if hex == HEX_GRID_CENTER then return false end
local blocking_towers = {}
local blocking_mobs = {}
local has_water = false

10
texture.lua

@ -10,11 +10,13 @@ local function load_texture(filepath)
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"),
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"),
@ -22,12 +24,18 @@ TEXTURES = {
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"),
CURTAIN = load_texture("res/curtain1.png"),
-- gui stuff
BUTTON1 = load_texture("res/button1.png"),
WIDER_BUTTON1 = load_texture("res/wider_button1.png"),
TAB_ICON = load_texture("res/tab_icon.png"),
GUI_SLIDER = load_texture("res/slider.png"),
GEAR = load_texture("res/gear.png"),
SELECT_BOX_ICON = load_texture("res/select_box.png"),
-- tower stuff
TOWER_WALL = load_texture("res/tower_wall.png"),

Loading…
Cancel
Save