2024-02-05 04:55:56 +00:00
|
|
|
board=klass()
|
|
|
|
|
2024-02-11 04:40:12 +00:00
|
|
|
function board:init(w)
|
|
|
|
local ruleset=w.ruleset
|
|
|
|
self.watcher=w
|
2024-02-05 06:11:17 +00:00
|
|
|
self.ruleset=ruleset
|
2024-02-05 04:55:56 +00:00
|
|
|
self.cursor=cursor:new(self)
|
2024-02-05 06:55:13 +00:00
|
|
|
self.animator=animator:new()
|
2024-02-10 06:35:55 +00:00
|
|
|
self.checkpoint=nil
|
2024-02-05 04:55:56 +00:00
|
|
|
self.slots={}
|
|
|
|
self.wells={}
|
2024-02-11 05:41:48 +00:00
|
|
|
self.restart_progress=0
|
2024-02-05 04:55:56 +00:00
|
|
|
|
|
|
|
-- board slots
|
2024-02-05 06:11:17 +00:00
|
|
|
-- ...n_slots: normal
|
|
|
|
for i=1,ruleset.n_slots do
|
|
|
|
add(self.slots,slot:new(ruleset))
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
2024-02-05 06:11:17 +00:00
|
|
|
-- n_slots+1: special
|
|
|
|
add(self.slots,slot:new(ruleset,1))
|
2024-02-05 04:55:56 +00:00
|
|
|
|
|
|
|
-- wells:
|
2024-02-05 06:11:17 +00:00
|
|
|
-- ...n_suits: wands, cups, swords, pentacles, etc
|
2024-02-05 04:55:56 +00:00
|
|
|
local function add_suit(_self,suit)
|
2024-02-10 06:17:57 +00:00
|
|
|
local w=well:new(_self.ruleset, function(lst,new)
|
2024-02-05 04:55:56 +00:00
|
|
|
assert(lst) -- the ace is always present initially
|
|
|
|
if (new.suit!=suit) return
|
|
|
|
return new.suit==suit and new.rank==lst.rank+1
|
2024-02-10 06:17:57 +00:00
|
|
|
end)
|
|
|
|
w.obscured_by_extra_slot=true
|
|
|
|
add(_self.wells, w)
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
2024-02-05 06:11:17 +00:00
|
|
|
for suit in all(self.ruleset.deck.suits) do
|
2024-02-05 04:55:56 +00:00
|
|
|
add_suit(self,suit)
|
|
|
|
end
|
2024-02-05 06:11:17 +00:00
|
|
|
-- n_suits+1: arcana ascending
|
|
|
|
add(self.wells,well:new(self.ruleset,function(lst,new)
|
2024-02-05 04:55:56 +00:00
|
|
|
if (new.suit!='a') return
|
|
|
|
if (not lst) return new.rank==0
|
|
|
|
return new.rank==lst.rank+1
|
|
|
|
end))
|
2024-02-11 21:44:45 +00:00
|
|
|
self.wells[#self.wells].obscured_by_last_well=true
|
2024-02-05 06:11:17 +00:00
|
|
|
-- n_suits+2: arcana descending
|
|
|
|
add(self.wells,well:new(self.ruleset,function(lst,new)
|
2024-02-05 04:55:56 +00:00
|
|
|
if (new.suit!='a') return
|
2024-02-05 06:11:17 +00:00
|
|
|
if (not lst) return new.rank==self.ruleset.n_arcana-1
|
2024-02-05 04:55:56 +00:00
|
|
|
return new.rank==lst.rank-1
|
|
|
|
end))
|
2024-02-11 21:44:45 +00:00
|
|
|
self.wells[#self.wells].obscured_by_last_well=true
|
|
|
|
-- top arcana
|
|
|
|
add(self.wells,well:new(self.ruleset,function(lst,new)
|
|
|
|
local w1=self.wells[self.ruleset.n_suits+1]
|
|
|
|
local w2=self.wells[self.ruleset.n_suits+2]
|
|
|
|
if (#w1.contents + #w2.contents != self.ruleset.n_arcana-1) return
|
|
|
|
return new.suit=='a'
|
|
|
|
end))
|
2024-02-05 04:55:56 +00:00
|
|
|
|
2024-02-11 04:40:12 +00:00
|
|
|
self:deal(w.seed or seeds:choose(self.ruleset.pool))
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
|
2024-02-11 21:44:45 +00:00
|
|
|
function board:get_endgame_card()
|
|
|
|
local last_arcana=self.wells[#self.wells]:peek()
|
|
|
|
if (last_arcana) return last_arcana
|
|
|
|
return self.last_card or 1
|
|
|
|
end
|
|
|
|
|
2024-02-10 02:13:39 +00:00
|
|
|
function board:deal(seed)
|
|
|
|
local deal=deal(self.ruleset,seed)
|
2024-02-05 06:11:17 +00:00
|
|
|
local n_usable_slots=self.ruleset.n_slots - 1
|
2024-02-09 01:04:33 +00:00
|
|
|
|
2024-02-05 06:11:17 +00:00
|
|
|
for i=1,#self.ruleset.deck.aces do
|
2024-02-05 04:55:56 +00:00
|
|
|
local well=self.wells[i]
|
2024-02-05 06:11:17 +00:00
|
|
|
local ace=self.ruleset.deck.aces[i]
|
2024-02-05 06:55:13 +00:00
|
|
|
self:animate_move_ace_to_well(ace,i)
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
|
2024-02-09 01:04:33 +00:00
|
|
|
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)
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-02-11 05:41:48 +00:00
|
|
|
function board:set_restart_progress(progress)
|
|
|
|
self.restart_progress=progress
|
|
|
|
end
|
|
|
|
|
2024-02-11 06:19:54 +00:00
|
|
|
function board:get_completion_level()
|
|
|
|
return self.ruleset.completion_level
|
|
|
|
end
|
|
|
|
|
2024-02-10 06:35:55 +00:00
|
|
|
function board:undo()
|
2024-02-12 04:54:01 +00:00
|
|
|
if (not self.checkpoint) return
|
|
|
|
if (not self.watcher:intercept("undo")) sounds:dire() return
|
|
|
|
sounds:menu()
|
|
|
|
self.checkpoint:apply(self)
|
2024-02-10 06:35:55 +00:00
|
|
|
self.checkpoint=nil
|
|
|
|
end
|
|
|
|
|
2024-02-05 06:55:13 +00:00
|
|
|
function board:on_idle()
|
|
|
|
self:find_automove()
|
|
|
|
end
|
|
|
|
|
2024-02-10 06:35:55 +00:00
|
|
|
function board:pre_move(card)
|
|
|
|
self.checkpoint=checkpoint:new(self,card)
|
|
|
|
end
|
|
|
|
|
2024-02-05 04:55:56 +00:00
|
|
|
function board:on_move()
|
2024-02-05 06:55:13 +00:00
|
|
|
self:find_automove()
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
|
2024-02-11 01:44:38 +00:00
|
|
|
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
|
|
|
|
|
2024-02-05 06:55:13 +00:00
|
|
|
function board:find_automove()
|
2024-02-05 04:55:56 +00:00
|
|
|
for s=1,#self.slots do
|
|
|
|
local top=self.slots[s]:peek()
|
|
|
|
if top then
|
2024-02-11 21:44:45 +00:00
|
|
|
for w=#self.wells,1,-1 do
|
2024-02-05 06:11:17 +00:00
|
|
|
if w<=self.ruleset.n_suits and self.slots[self.ruleset.n_slots+1]:peek()!=nil then
|
2024-02-05 04:55:56 +00:00
|
|
|
-- the top wells are blocked
|
|
|
|
elseif self.wells[w]:would_accept(top) then
|
2024-02-05 06:55:13 +00:00
|
|
|
self:animate_and_move_to_well(s,w)
|
2024-02-11 21:44:45 +00:00
|
|
|
self.last_card=top
|
2024-02-05 06:55:13 +00:00
|
|
|
|
2024-02-05 04:55:56 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-02-05 06:55:13 +00:00
|
|
|
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
|
|
|
|
|
2024-02-05 04:55:56 +00:00
|
|
|
function board:draw()
|
2024-02-10 06:17:57 +00:00
|
|
|
local extra_slot_full=self.slots[self.ruleset.n_slots+1]:peek()!=nil
|
2024-02-11 21:44:45 +00:00
|
|
|
local last_well_full=self.wells[#self.wells]:peek()!=nil
|
2024-02-05 04:55:56 +00:00
|
|
|
for w_ix=1,#self.wells do
|
|
|
|
local w=self.wells[w_ix]
|
2024-02-05 06:11:17 +00:00
|
|
|
local l=self.ruleset.layouts:well(w_ix)
|
2024-02-05 04:55:56 +00:00
|
|
|
|
2024-02-10 06:17:57 +00:00
|
|
|
local shadowed=nil
|
2024-02-11 21:44:45 +00:00
|
|
|
if w.obscured_by_last_well and last_well_full then
|
|
|
|
shadowed=true
|
|
|
|
end
|
2024-02-10 06:17:57 +00:00
|
|
|
if w.obscured_by_extra_slot and extra_slot_full then
|
|
|
|
shadowed=true
|
|
|
|
end
|
|
|
|
|
2024-02-05 04:55:56 +00:00
|
|
|
for i=1,#w.contents do
|
|
|
|
local x,y=l:place_card(i)
|
2024-02-11 21:44:45 +00:00
|
|
|
self.ruleset.deck:draw_card(x,y,w.contents[i],{rotate=l.rotated,shadowed=shadowed})
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function forall_slots(cb)
|
|
|
|
for s_ix=1,#self.slots do
|
|
|
|
local s=self.slots[s_ix]
|
2024-02-05 06:11:17 +00:00
|
|
|
local l=self.ruleset.layouts:slot(s_ix)
|
2024-02-05 04:55:56 +00:00
|
|
|
|
|
|
|
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)
|
2024-02-10 06:17:57 +00:00
|
|
|
self.ruleset.deck:draw_card(x,y,s.contents[i],{rotate=l.rotated})
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2024-02-10 06:35:55 +00:00
|
|
|
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
|
|
|
|
|
2024-02-05 06:55:13 +00:00
|
|
|
self.animator:draw()
|
|
|
|
|
|
|
|
if self.animator:idle() then
|
2024-02-11 04:56:48 +00:00
|
|
|
self.watcher:draw(self)
|
2024-02-05 06:55:13 +00:00
|
|
|
local hover_slot=self.cursor:hover_slot()
|
|
|
|
forall_slots(function(x,y,s_ix,s,l,n)
|
|
|
|
if hover_slot==s_ix then
|
2024-02-11 04:56:48 +00:00
|
|
|
self.cursor:draw_at(l,n)
|
2024-02-05 06:55:13 +00:00
|
|
|
end
|
|
|
|
end)
|
2024-02-11 04:40:12 +00:00
|
|
|
end
|
2024-02-11 05:41:48 +00:00
|
|
|
|
|
|
|
democrap:distort_screen(self.restart_progress)
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
slot=klass()
|
2024-02-05 06:11:17 +00:00
|
|
|
function slot:init(ruleset,max_n)
|
|
|
|
self.ruleset=ruleset
|
2024-02-05 04:55:56 +00:00
|
|
|
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
|
2024-02-05 06:11:17 +00:00
|
|
|
lst=self.ruleset.deck.cards[lst]
|
|
|
|
new=self.ruleset.deck.cards[new]
|
2024-02-05 04:55:56 +00:00
|
|
|
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()
|
2024-02-05 06:11:17 +00:00
|
|
|
function well:init(ruleset,accept_cb)
|
|
|
|
self.ruleset=ruleset
|
2024-02-05 04:55:56 +00:00
|
|
|
self.accept_cb=accept_cb
|
|
|
|
self.contents={}
|
|
|
|
end
|
|
|
|
function well:would_accept(new)
|
|
|
|
local lst=self.contents[#self.contents]
|
2024-02-05 06:11:17 +00:00
|
|
|
if (lst) lst=self.ruleset.deck.cards[lst]
|
2024-02-05 04:55:56 +00:00
|
|
|
assert(new,"must be populated")
|
2024-02-05 06:11:17 +00:00
|
|
|
new=self.ruleset.deck.cards[new]
|
2024-02-05 04:55:56 +00:00
|
|
|
assert(new,"must be populated")
|
|
|
|
return self.accept_cb(lst,new)
|
|
|
|
end
|
|
|
|
function well:add(card)
|
|
|
|
add(self.contents,card)
|
2024-02-05 06:55:13 +00:00
|
|
|
end
|
|
|
|
function well:clear()
|
|
|
|
self.contents={}
|
2024-02-11 21:44:45 +00:00
|
|
|
end
|
|
|
|
function well:peek()
|
|
|
|
return self.contents[#self.contents]
|
2024-02-05 04:55:56 +00:00
|
|
|
end
|