First commit

This commit is contained in:
2023-08-12 11:36:14 +02:00
commit 7c537e3c4f
37 changed files with 5252 additions and 0 deletions
+109
View File
@@ -0,0 +1,109 @@
function state_game_gameover()
local box
local event_bus
local y = 90
local buttons = {
-- {
-- hover = false,
-- label = "Play Again",
-- box = { x = 120 - prn_len("Play Again") / 2, y = y, width = prn_len("Play Again"), height = 10 },
-- cb = function()
-- trace("again")
-- end
-- },
{
hover = false,
label = "Main Menu",
box = { x = 120 - prn_len("Main Menu") / 2, y = y + 15, width = prn_len("Main Menu"), height = 10 },
cb = function()
reset()
end
}
}
local function open_box()
while box.w < 180 do
box.w = box.w * 1.1
waitframes(1)
end
if box.w > 180 then
box.w = 180
end
while box.h < 20 do
box.h = box.h + 1
waitframes(1)
end
end
local function show_buttons()
-- show a button "again", and another "main menu"
-- right under the box
for _, button in ipairs(buttons) do
button.hover = false
-- rect(button.box.x - 1, button.box.y - 1, button.box.width + 2, button.box.height + 2, 2)
-- Hover button
if mouse_in_rect(button.box) then
button.hover = true
-- Click button
if Input:mouse_released()
then
button.cb()
end
end
prn_border(button.label,
button.box.x, button.box.y,
button.hover and 12 or 13,
ALIGN.Left,
0)
end
end
local function update(self)
-- draw box
rect(120 - box.w / 2, 67 - box.h / 2, box.w, box.h, 0)
rectb(120 - box.w / 2, 67 - box.h / 2, box.w, box.h, 15)
clip(120 - box.w / 2, 67 - box.h / 2, box.w, box.h)
-- print who won according to score
local text
local color
local score_player = get_total_score(SCORES.player)
local score_enemy = get_total_score(SCORES.enemy)
if score_player > score_enemy then
color = COLOR_PLAYER
text = "YOU WON " .. score_player .. " - " .. score_enemy
elseif score_player < score_enemy then
color = COLOR_ENEMY
text = "YOU LOST " .. score_player .. " - " .. score_enemy
else
color = 14
text = "DRAW " .. score_player .. " - " .. score_enemy
end
prn_border(text, 120, 67 - 3, 12, ALIGN.Center, color)
clip()
show_buttons()
end
---comment
---@param self any
---@param die Die
---@param _event_bus EventBus
---@param turn "player"|"enemy"
local function _enter(self, die, _event_bus, turn)
assert(_event_bus, "event_bus is nil")
assert(turn, "turn is nil")
event_bus = _event_bus
event_bus:emit(EVENT_REMOVE_DIE)
box = {
h = 0, w = 1
}
addcoroutine(open_box)
end
return { update = update, _enter = _enter }
end
+91
View File
@@ -0,0 +1,91 @@
function state_game_loading()
local covering
local event_bus
local turn
local banner
local function show_transition()
-- slightly scale up the transition dice
for i, die in ipairs(covering) do
die.scale = die.scale - 0.03 / i * 80
die.angle = die.angle - i / 50
if die.scale < 0 then
die.scale = 0
end
swap_color(12, 15)
swap_color(13, 15)
swap_color(14, 15)
swap_color(0, 15)
die:draw(-1)
end
-- clean
if table.count(covering, function(d) return d.scale == 0 end) == #covering then
covering = {}
end
end
local function _enter(self, data)
assert(data.covering, "needs data.covering")
assert(data.event_bus, "needs data.event_bus")
covering = data.covering
event_bus = data.event_bus
turn = data.turn
banner = {
h = 0,
w = 1,
}
addcoroutine(function()
while banner.w < 240 do
banner.w = banner.w *1.1
waitframes(1)
end
if banner.w > 240 then
banner.w = 240
end
while banner.h < 20 do
banner.h = banner.h + 1
waitframes(1)
end
waitsecs(1.5)
while banner.h > 0.5 do
banner.h = banner.h - 1
waitframes(1)
end
while banner.w > 1 do
banner.w = banner.w * 0.9
waitframes(1)
end
waitsecs(0.5)
event_bus:emit(EVENT_RESET_DIE)
event_bus:emit(EVENT_SET_STEP, "rolling")
end)
end
local function update(self)
-- black banner in the middle
rect(120 - banner.w / 2, 67 - banner.h / 2, banner.w, banner.h, 0)
rectb(120 - banner.w / 2, 67 - banner.h / 2, banner.w, banner.h, 15)
clip(120 - banner.w / 2, 67 - banner.h / 2, banner.w, banner.h)
-- show who's going first
local text
local color
if turn == "player" then
color = COLOR_PLAYER
text = "You begin this round"
else
color = COLOR_ENEMY
text = "Your opponent begins this round"
end
prn_border(text, 120, 67 - 4, 12, ALIGN.Center, color)
clip()
if #covering > 0 then
show_transition()
end
end
return { _enter = _enter, update = update }
end
+102
View File
@@ -0,0 +1,102 @@
include "ai"
function state_game_placing()
--- @type Die
local die
--- @type EventBus
local event_bus
--- @type 'player'|'enemy'
local turn
local placing = false
local waiting = 30
--- Click on a column to place the die
local function place_die(col_id)
if placing then return end
placing = true
local cell_id = 0
for i = 1, 3 do
if PLACED_DICE[turn][col_id][i] == 0 then
cell_id = i
break
end
end
step = "waiting"
addcoroutine(function()
-- move the die to its cell
local cell = get_cell_coords(turn, col_id, cell_id)
die.box = nil
die.angle = die.angle % 360
-- get value closest to 0. e.g. 270 -> -90
if die.angle > 180 then
die.angle = die.angle - 360
end
local tween = Tween.new(0.5, die, { x = cell.x + 3, y = cell.y + 1, angle = 0 }, "outQuad")
repeat
coroutine.yield()
until tween:update(dt)
PLACED_DICE:set(turn, col_id, cell_id, die.value)
-- Animate the dice and update the scores
event_bus:emit(EVENT_REMOVE_DIE)
event_bus:emit(EVENT_SET_STEP, "scoring")
end)
end
---@param self any
---@param _die Die
---@param _event_bus EventBus
---@param _turn 'player'|'enemy'
local _enter = function(self, _die, _event_bus, _turn)
assert(_die, "missing die")
assert(_event_bus, "missing event_bus")
assert(_turn, "missing turn")
die = _die
event_bus = _event_bus
turn = _turn
waiting = 30
placing = false
end
local update = function(self)
waiting = waiting - 1
if turn == "enemy" then
if waiting <= 0 and not placing then
local col = ai_pick_column(PLACED_DICE.enemy, PLACED_DICE.player, die.value)
place_die(col)
end
else -- turn == "player"
-- Highlight the hovered column
local columns = {
get_column_rect(turn, 1),
get_column_rect(turn, 2),
get_column_rect(turn, 3),
}
local col, col_id = table.find(columns, function(c)
return mouse_in_rect(c)
end)
if col then
if PLACED_DICE:has_free_slot(turn, col_id) then
-- draw hovered column
rect(col.x, col.y, col.width, col.height, 14)
-- place the die if clicked
if Input:mouse_released() then
place_die(col_id)
end
end
end
end
-- Redraw the board because the hovered column is drawn on top of it
-- and i'm too lazy to fix this properly.
draw_board()
end
return {
_enter = _enter,
update = update,
}
end
+40
View File
@@ -0,0 +1,40 @@
function state_game_rolling()
--- @type Die
local die
--- @type EventBus
local event_bus
local function roll_die()
die.bouncing = true
local rot = Tween.new(0.7, die, {
angle = (die.angle + math.random(180,360))-- die.angle + (math.random()<.5 and 1 or -1)* math.random(270, 360)
}, "outSine")
local done = false
repeat
done = rot:update(dt)
coroutine.yield()
until done
die.bouncing = false
event_bus:emit(EVENT_SET_STEP, "placing")
end
---@param self any
---@param _die Die
---@param _event_bus EventBus
local _enter = function(self, _die, _event_bus)
assert(_die, "states.game.rolling needs a die")
assert(_event_bus, "states.game.rolling needs an event_bus")
die = _die
event_bus = _event_bus
addcoroutine(roll_die)
end
local update = function(self)
end
return {
_enter = _enter,
update = update
}
end
+93
View File
@@ -0,0 +1,93 @@
function state_game_scoring()
local dice_to_destroy = {}
--- @type EventBus
local event_bus
local destroyed = 0
local function update_board_and_score(player, col, row)
assert(player)
assert(col)
assert(row)
-- Create a Die and place it on the cell
local cell = get_cell_coords(player, col, row)
local die = Die:new({ value = PLACED_DICE:get(player, col, row), x = cell.x + 3, y = cell.y + 1 })
-- Set the score to 0
PLACED_DICE:set(player, col, row, 0)
table.insert(dice_to_destroy, die)
addcoroutine(function()
waitframes(10)
local tween = Tween.new(0.5, die, { scale = 0.1 }, "inOutQuad")
repeat
coroutine.yield()
until tween:update(dt)
table.remove_item(dice_to_destroy, die)
end)
end
---comment
---@param self any
---@param _die Die|nil
---@param _event_bus EventBus
---@param player 'player'|'enemy'
local function _enter(self, _die, _event_bus, player)
assert(_event_bus)
assert(player == "player" or player == "enemy")
event_bus = _event_bus
destroyed = 0
local other_player = player == "player" and "enemy" or "player"
-- Check if the newly placed die must remove other dice
local current_dice = PLACED_DICE[player]
local other_dice = PLACED_DICE[other_player]
-- Check the dice for each column, and remove corresponding dice in the opposing column
for col = 1, 3 do
for row = 1, 3 do
local value = current_dice[col][row]
for row2 = 1, 3 do
local value2 = other_dice[col][row2]
if value > 0 and value2 > 0 and value2 == value then
trace("destroy " .. other_player .. " " .. col .. " " .. row2 .. " = " .. value2)
update_board_and_score(other_player, col, row2)
destroyed = destroyed + 1
end
end
end
end
-- Update the scores and move on to next state
addcoroutine(function()
waitsecs((destroyed > 0) and 0.6 or 0.3)
SCORES:update()
-- Check if it's game over
if PLACED_DICE:is_game_over() then
SCORES:update()
event_bus:emit(EVENT_SET_STEP, "game_over")
else
event_bus:emit(EVENT_CHANGE_TURN)
end
end)
end
local function update(self)
for _, die in ipairs(dice_to_destroy) do
die:draw()
end
end
local function _leave(self)
end
return {
_enter = _enter,
update = update,
_leave = _leave
}
end