228 lines
5.0 KiB
Lua
228 lines
5.0 KiB
Lua
board=klass()
|
|
|
|
function board:init(w)
|
|
local ruleset=w.ruleset
|
|
self.watcher=w
|
|
self.ruleset=ruleset
|
|
self.cursor=cursor:new(self)
|
|
self.animator=animator:new()
|
|
self.checkpoint=nil
|
|
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)
|
|
local w=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)
|
|
w.obscured_by_extra_slot=true
|
|
add(_self.wells, w)
|
|
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(w.seed or seeds:choose(self.ruleset.pool))
|
|
end
|
|
|
|
function board:deal(seed)
|
|
local deal=deal(self.ruleset,seed)
|
|
local n_usable_slots=self.ruleset.n_slots - 1
|
|
|
|
for i=1,#self.ruleset.deck.aces do
|
|
local well=self.wells[i]
|
|
local ace=self.ruleset.deck.aces[i]
|
|
self:animate_move_ace_to_well(ace,i)
|
|
end
|
|
|
|
local rows=#deal[1]
|
|
for y=1,rows do
|
|
for x=1,#deal do
|
|
local outx=x
|
|
if (x>n_usable_slots\2) outx+=1
|
|
self:animate_move_new_card_to_slot(deal[x][y],outx)
|
|
end
|
|
end
|
|
end
|
|
|
|
function board:undo()
|
|
if (not self.watcher:intercept("undo")) return
|
|
if (self.checkpoint) self.checkpoint:apply(self)
|
|
self.checkpoint=nil
|
|
end
|
|
|
|
function board:on_idle()
|
|
self:find_automove()
|
|
end
|
|
|
|
function board:pre_move(card)
|
|
self.checkpoint=checkpoint:new(self,card)
|
|
end
|
|
|
|
function board:on_move()
|
|
self:find_automove()
|
|
end
|
|
|
|
function board:is_won()
|
|
if (not self:can_take_input()) return false
|
|
for s=1,#self.slots do
|
|
if (self.slots[s]:peek()) return false
|
|
end
|
|
return true
|
|
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()
|
|
local extra_slot_full=self.slots[self.ruleset.n_slots+1]:peek()!=nil
|
|
for w_ix=1,#self.wells do
|
|
local w=self.wells[w_ix]
|
|
local l=self.ruleset.layouts:well(w_ix)
|
|
|
|
local shadowed=nil
|
|
if w.obscured_by_extra_slot and extra_slot_full then
|
|
shadowed=true
|
|
end
|
|
|
|
for i=1,#w.contents do
|
|
local x,y=l:place_card(i)
|
|
self.ruleset.deck:draw_card(x,y,w.contents[i],{shadowed=shadowed})
|
|
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],{rotate=l.rotated})
|
|
end
|
|
end)
|
|
|
|
if self.checkpoint then
|
|
local cpl=self.ruleset.layouts:checkpoint()
|
|
local x,y=cpl:place_card(0)
|
|
self.ruleset.deck:draw_card(x,y,self.checkpoint.card,{shadowed=true})
|
|
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
|
|
self.cursor:draw_at(l,n+1)
|
|
end
|
|
end)
|
|
|
|
self.watcher:draw(self)
|
|
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 |