234 lines
5.1 KiB
Lua
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 |