fortunes_foundation/board.lua
2024-02-07 21:34:11 -08:00

234 lines
5.1 KiB
Lua

board=klass()
function board:init(ruleset)
self.ruleset=ruleset
self.cursor=cursor:new(self)
self.animator=animator:new()
self.slots={}
self.wells={}
-- board slots
-- ...n_slots: normal
for i=1,ruleset.n_slots do
add(self.slots,slot:new(ruleset))
end
-- n_slots+1: special
add(self.slots,slot:new(ruleset,1))
-- wells:
-- ...n_suits: wands, cups, swords, pentacles, etc
local function add_suit(_self,suit)
add(_self.wells,well:new(_self.ruleset, function(lst,new)
assert(lst) -- the ace is always present initially
if (new.suit!=suit) return
return new.suit==suit and new.rank==lst.rank+1
end))
end
for suit in all(self.ruleset.deck.suits) do
add_suit(self,suit)
end
-- n_suits+1: arcana ascending
add(self.wells,well:new(self.ruleset,function(lst,new)
if (new.suit!='a') return
if (not lst) return new.rank==0
return new.rank==lst.rank+1
end))
-- n_suits+2: arcana descending
add(self.wells,well:new(self.ruleset,function(lst,new)
if (new.suit!='a') return
if (not lst) return new.rank==self.ruleset.n_arcana-1
return new.rank==lst.rank-1
end))
self:deal()
end
function board:deal()
local n_usable_slots=self.ruleset.n_slots - 1
-- first, pull the aces
local available={}
for card=1,#self.ruleset.deck.cards do
available[card]=true
end
for i=1,#self.ruleset.deck.aces do
local well=self.wells[i]
local ace=self.ruleset.deck.aces[i]
well:add(ace) -- temporarily, so would_accept will work
self:animate_move_ace_to_well(ace,i)
available[ace]=false
end
local eligible_bottom_row={}
for card=1,#self.ruleset.deck.cards do
local skip
if not available[card] then
skip=true
else
for w in all(self.wells) do
if (w:would_accept(card)) skip=true break
end
end
if (not skip) add(eligible_bottom_row,card)
end
-- let the animation install the aces for real
for w in all(self.wells) do
w:clear()
end
function i_to_slot(i)
if (i<n_usable_slots\2) return i+1
return i+2
end
local bottom_row={}
shuf(eligible_bottom_row)
for i=1,n_usable_slots do
local card=eligible_bottom_row[i]
add(bottom_row,card)
available[card]=false
end
eligible={}
for card=1,#self.ruleset.deck.cards do
if (available[card]) add(eligible,card)
end
shuf(eligible)
for card in all(bottom_row) do
add(eligible,card)
end
for i=1,#eligible do
local ix=i_to_slot((i-1)%n_usable_slots)
local slot=self.slots[ix]
self:animate_move_new_card_to_slot(eligible[i],ix)
end
end
function board:on_idle()
self:find_automove()
end
function board:on_move()
-- TODO: Make checkpoint
self:find_automove()
end
function board:find_automove()
for s=1,#self.slots do
local top=self.slots[s]:peek()
if top then
for w=1,#self.wells do
if w<=self.ruleset.n_suits and self.slots[self.ruleset.n_slots+1]:peek()!=nil then
-- the top wells are blocked
elseif self.wells[w]:would_accept(top) then
self:animate_and_move_to_well(s,w)
return true
end
end
end
end
end
function board:can_take_input()
return self.animator:idle()
end
function board:update()
local was_idle=self.animator:idle()
self.animator:update()
local is_idle=self.animator:idle()
if (not was_idle and is_idle) then
self:on_idle()
end
end
function board:draw()
for w_ix=1,#self.wells do
local w=self.wells[w_ix]
local l=self.ruleset.layouts:well(w_ix)
for i=1,#w.contents do
local x,y=l:place_card(i)
self.ruleset.deck:draw_card(x,y,w.contents[i],i<#w.contents)
end
end
local function forall_slots(cb)
for s_ix=1,#self.slots do
local s=self.slots[s_ix]
local l=self.ruleset.layouts:slot(s_ix)
local n=#s.contents
if (self.cursor.grabbed==s_ix) n-=1
cb(x,y,s_ix,s,l,n)
end
end
forall_slots(function(x,y,s_ix,s,l,n)
for i=1,n do
local x,y=l:place_card(i)
self.ruleset.deck:draw_card(x,y,s.contents[i],i<n)
end
end)
self.animator:draw()
if self.animator:idle() then
local hover_slot=self.cursor:hover_slot()
forall_slots(function(x,y,s_ix,s,l,n)
if hover_slot==s_ix then
local x,y=l:place_card(n+1)
self.cursor:draw_at(x,y)
end
end)
end
end
slot=klass()
function slot:init(ruleset,max_n)
self.ruleset=ruleset
self.contents={}
self.max_n=max_n
end
function slot:would_accept(new)
local n=#self.contents
if (n==self.max_n) return
local lst=self.contents[n]
if (not lst) return true
lst=self.ruleset.deck.cards[lst]
new=self.ruleset.deck.cards[new]
return lst.suit==new.suit and (lst.rank==new.rank-1 or lst.rank==new.rank+1)
end
function slot:add(card)
add(self.contents,card)
end
function slot:peek()
return self.contents[#self.contents]
end
function slot:pop()
return deli(self.contents,#self.contents)
end
well=klass()
function well:init(ruleset,accept_cb)
self.ruleset=ruleset
self.accept_cb=accept_cb
self.contents={}
end
function well:would_accept(new)
local lst=self.contents[#self.contents]
if (lst) lst=self.ruleset.deck.cards[lst]
assert(new,"must be populated")
new=self.ruleset.deck.cards[new]
assert(new,"must be populated")
return self.accept_cb(lst,new)
end
function well:add(card)
add(self.contents,card)
end
function well:clear()
self.contents={}
end