[PROTOTYPE] CS's Solitaire Switch: Outsmart the Odds in This Experimental Strategy Game!

Hey everyone, find the verse code at the bottom of this message!!!

I’d love to introduce you to something a little different — Solitaire Switch, a fresh take on card-based gameplay that’s as much about logic and momentum as it is about luck. It started as a prototype to explore card mechanics and mouse interactions… but it’s grown into a quirky, buggy, and surprisingly addictive little game that’s ready for you to try.

CS3VIDEO

Your goal? Simple in theory — brutal in practice.
Play every single card from the deck onto the board. Leave even one card behind in any hand, and it’s game over. The real challenge isn’t just your own strategy — it’s managing the rhythm of the table and working with (or around) the indifferent AI that plays strictly by logic. Left on their own, four AIs win only about 1 in 10 games. You? You can do better.

C3SControls

Controls:
Mouse: Aim-and-click, with adjustable sensitivity.
Controller: Full gamepad support with intuitive UI navigation.

Yes, it’s buggy. Yes, it’s rough around the edges. But underneath it all is a playable card game with real strategic bite, emergent strategies, and a satisfying challenge — especially if you enjoy solving puzzles, mastering patterns, or out-thinking the system.

If any of that sounds like your kind of fun, give Solitaire Switch a try! It’s not polished, but it’s experimental, it’s playable, and once it clicks… it’s hard to put down.

P͟l͟a͟y͟ ͟i͟t͟ ͟h͟e͟r͟e͟:͟ 1217-1574-4756

Thanks for checking it out, if there is interest then expect to see an improved version coming soon with my future projects!

Verse Code

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
using { /Verse.org/Assets }
using { /Verse.org/Random }

PolarToCartesian(X:float, Y:float, Z:float):vector3 := return vector3{X:=Z, Y:=X*5.3333333333333333, Z:=Y*6.0}
PolarToCartesianWorld(X:float, Y:float, Z:float):vector3 := return vector3{X:=Z, Y:=X*5.3333333333333333, Z:=1024.0+Y*6.0}
Tree := interface:
    GetZone(InX:float,InY:float):?BlankZone
    
BlankZone := class<unique>(Tree):
    XMin:float
    XMax:float
    YMin:float
    YMax:float
    GetZone<override>(InX:float,InY:float):?BlankZone:=return option{Self}
    SelectorInteraction():void:=return
    CursorLeftFocus(Pointer:CursorHandler):void:=return
    CursorEnteredFocus(Pointer:CursorHandler):void:=return
    CursorClick(Pointer:CursorHandler, X:float, Y:float):void:=return
    CursorRelease(Pointer:CursorHandler, X:float, Y:float):void:=return
    DropHoldable(Obj:Grab_Obj):void:=
        Obj.ReturnHome()
    DropHoldable(Obj:Grab_Obj, X:float, Y:float):void:=DropHoldable(Obj)
    RemoveHomeLink(Obj:Grab_Obj):void:=return
    SelectorUp():void:=return
    SelectorDown():void:=return
    SelectorLeft():void:=return
    SelectorRight():void:=return
    GetNextZone(BaseTree:Tree,X:float, Y:float):?BlankZone:=
        if(XMin <= X <= XMax and YMin <= Y <= YMax){return false}
        else{return BaseTree.GetZone(X,Y)}

GameToggleButtonZone := class(BlankZone, Focusable):
    BackRef:CCGDemoController
    BaseProp:creative_prop
    ButtonPos:vector3
    LeftLink:BlankZone
    GetFocalTransform<override>()<transacts>:transform := return transform{Translation := ButtonPos ,Scale :=vector3{X:=1.0, Y:=1.5, Z:=0.3}, Rotation := IdentityRotation()}
    CursorLeftFocus<override>(Pointer:CursorHandler):void:=
        BaseProp.SetMaterial(Materials.PlayGreen1)#spawn{BaseProp.MoveTo(transform{Translation := vector3{X:=-960.0, Y:=-464.5, Z:=1172.5}, Scale := vector3{X:=1.0, Y:=207.0, Z := 297.0}},0.5)}
        Pointer.UpdateStyle(CursorStyle.Standard)
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=
        BaseProp.SetMaterial(Materials.SideGreen)#spawn{BaseProp.MoveTo(transform{Translation := vector3{X:=-960.0, Y:=-464.5, Z:=1172.5}, Scale := vector3{X:=1.0, Y:=414.0, Z := 594.0}},0.5)}
        Pointer.UpdateStyle(CursorStyle.Pointing)
    CursorClick<override>(Pointer:CursorHandler, X:float, Y:float):void:=SelectorInteraction()
    SelectorInteraction<override>():void:=
        BackRef.ToggleInfoState()
    SelectorLeft<override>():void:=
        if(Selector := BackRef.Controller.SelectorRef?; Left := Focusable[LeftLink]){Selector.SetFocus(Left)}

InfoToggleButtonZone := class(BlankZone, Focusable):
    BackRef:CCGDemoController
    BaseProp:creative_prop
    ButtonPos:vector3
    LeftLink:BlankZone
    GetFocalTransform<override>()<transacts>:transform := return transform{Translation := ButtonPos ,Scale :=vector3{X:=1.0, Y:=1.5, Z:=0.3}, Rotation := MakeRotationFromYawPitchRollDegrees(180.0,0.0,0.0)}

    CursorLeftFocus<override>(Pointer:CursorHandler):void:=
        BaseProp.SetMaterial(Materials.PlayGreen1)#spawn{BaseProp.MoveTo(transform{Translation := vector3{X:=-960.0, Y:=-464.5, Z:=1172.5}, Scale := vector3{X:=1.0, Y:=207.0, Z := 297.0}},0.5)}
        Pointer.UpdateStyle(CursorStyle.Standard)
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=
        BaseProp.SetMaterial(Materials.SideGreen)#spawn{BaseProp.MoveTo(transform{Translation := vector3{X:=-960.0, Y:=-464.5, Z:=1172.5}, Scale := vector3{X:=1.0, Y:=414.0, Z := 594.0}},0.5)}
        Pointer.UpdateStyle(CursorStyle.Pointing)
    CursorClick<override>(Pointer:CursorHandler, X:float, Y:float):void:=SelectorInteraction()
    SelectorInteraction<override>():void:=
        BackRef.ToggleInfoState()
    SelectorRight<override>():void:=
        if(Selector := BackRef.Controller.SelectorRef?; Left := LevelZone[LeftLink]):
            Selector.SetFocus(Left)
            if(LZone := LevelZone[LeftLink]):
                LZone.EnteredZone()

LevelZone := class(BlankZone, Focusable):
    BackRef:CCGDemoController
    PortalLink:matchmaking_portal_device
    Frame:creative_prop
    PictureMat:material
    VideoMat:material
    Pos:vector3
    var LeftLink:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var RightLink:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    GetFocalTransform<override>()<transacts>:transform := transform{Translation := Pos ,Scale :=vector3{X:=1.0, Y:=1.75, Z:=0.75}, Rotation := MakeRotationFromYawPitchRollDegrees(180.0,0.0,0.0)}
    CursorLeftFocus<override>(Pointer:CursorHandler):void:=LeftZone()
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=EnteredZone()
    SelectorLeft<override>():void:=
        if(Selector := BackRef.Controller.SelectorRef?; Left := Focusable[LeftLink]):
            Selector.SetFocus(Left)
            if(LZone := LevelZone[LeftLink]):
                LeftZone()
                LZone.EnteredZone()
    SelectorRight<override>():void:=
        if(Selector := BackRef.Controller.SelectorRef?; Right := Focusable[RightLink]):
            Selector.SetFocus(Right)
            if(LZone := LevelZone[RightLink]):
                LeftZone()
                LZone.EnteredZone()
    EnteredZone():void:=
        PortalLink.Enable()
        BackRef.ShowInteract.Enable()
        BackRef.HideInteract.Disable()
        Frame.SetMaterial(VideoMat)
    LeftZone():void:=
        PortalLink.Disable()
        BackRef.HideInteract.Enable()
        BackRef.ShowInteract.Disable()
        Frame.SetMaterial(PictureMat)

PlayZone := class(BlankZone):
    BackRef:CCGDemoController
    var PhysicalStack:[]Physical_Card := array{}
    var VirtualStack:[]Virtual_Card := array{}
    BaseProp:creative_prop
    CursorLeftFocus<override>(Pointer:CursorHandler):void:=BaseProp.SetMaterial(Materials.PlayGreen1)#spawn{BaseProp.MoveTo(transform{Translation := vector3{X:=-960.0, Y:=-464.5, Z:=1172.5}, Scale := vector3{X:=1.0, Y:=207.0, Z := 297.0}},0.5)}
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=BaseProp.SetMaterial(Materials.SideGreen)#spawn{BaseProp.MoveTo(transform{Translation := vector3{X:=-960.0, Y:=-464.5, Z:=1172.5}, Scale := vector3{X:=1.0, Y:=414.0, Z := 594.0}},0.5)}
    DropHoldable<override>(Obj:Grab_Obj):void:=DropHoldable(Obj, -87.28125+GetRandomFloat(-40.0, 40.0),24.625+GetRandomFloat(-12.0, 12.0))
    DropHoldable<override>(Obj:Grab_Obj, X:float, Y:float):void:=
        if(Card := Physical_Card[Obj], Card.HomeZone = BlankZone[Self]){return}
        else if(Sol := SolitairSwitch[BackRef.GameSession],Card := Physical_Card[Obj], Card.HomeZone = Sol.CurrentPlayer(1),BackRef.Controller.SelectorRef?.Lock = false, Sol.PickupState = 0 or Card.Value = 13 or Card.Value = 2):
            Turn := BackRef.GameSession.ApplyTurn(BackRef.HandArea,option{Card})
            if(Turn = true):
                AddCard(Card, X, Y)
        else if(Card := Physical_Card[Obj],BZone := Card.HomeZone, BZone <> BackRef.HandArea):
            AddCard(Card, X, Y)
        if(Card := Physical_Card[Obj]):
            Card.ReturnHome()
    AddCard(Card:Physical_Card, X:float, Y:float):void:=
        if(PhysicalStack.Length > 15, Transfer := PhysicalStack.Slice[0,8], Rest := PhysicalStack.Slice[8]):
            for(Index:=0..Rest.Length-1,MoveCard:=Rest[Index]){Pos:=MoveCard.CardProp.GetTransform().Translation;spawn{MoveCard.MoveToRightUp(vector3{X:=-962.0-(Index*1.0), Y:=Pos.Y, Z:= Pos.Z},vector3{X:=1.0,Y:=1.0,Z:=1.0})}}
            set PhysicalStack = Rest
            set VirtualStack += for(RemoveCard:Transfer){RemoveCard.Virtualise()}
        set PhysicalStack += array{Card}
        if(Card.CardProp.TeleportTo[Card.CardProp.GetTransform().Translation-vector3{X:=103.5}, Card.Rotation]):
            spawn:
                Card.MoveToRightUp(vector3{X:=-962.0-(PhysicalStack.Length*1.0), Y:=((X+87.28125-16.132653)) - 368.5, Z:= ((Y-24.625)) + 1108.5},vector3{X:=1.0,Y:=1.0,Z:=1.0})    
        Card.SetHome(Self)

    ReturnCards():[]Virtual_Card:=
        Out := VirtualStack + for(Card:PhysicalStack){Card.Virtualise()}
        set PhysicalStack = array{}
        set VirtualStack = array{}
        return Out

HandZone := class(BlankZone, Focusable):
    BackRef:CCGDemoController
    BaseProp:creative_prop
    var HandArray:[]Physical_Card := array{}

    var SelectedCard:?Physical_Card := false
    var LastIndex:?int := false
    UpdateDeckPlacement():void:=if(HandArray.Length <> 0){for(Index := 0..HandArray.Length; Card := HandArray[Index]){spawn{Card.MoveToRightUp(vector3{X:=-962.0, Y:=(Index*207.0)-851.5, Z:=640.0})}}}
    IsOverCard(X:float):void:=
        if(Cursor := BackRef.Controller.CursorRef?){if(Index := Floor[(X+180.0)*0.02576489533],Index<=HandArray.Length-1):
            if(HasIndex := LastIndex?):
                if(Index <> LastIndex?; Card := HandArray[Index]):
                    set LastIndex = option{Index}
                    BackRef.UpdateSelected.Signal(option{Card})
            else if(Card := HandArray[Index]):
                set LastIndex = option{Index}
                BackRef.UpdateSelected.Signal(option{Card})
            Cursor.UpdateStyle(CursorStyle.Pointing)
        else{Cursor.UpdateStyle(CursorStyle.Standard)}}
    RemoveCard(OldCard:Virtual_Card):void:=
        if(SelectedCard? = OldCard){set SelectedCard = false}
        set HandArray = for(Card:HandArray, Card <> OldCard){Card}
        UpdateDeckPlacement()
    GetCardPosition(Card:Physical_Card)<transacts>:vector3:=
        for(Index := 0..HandArray.Length-1,TCard:=HandArray[Index], TCard = Card){return vector3{X:=-963.0, Y:=(Index*207.0)-851.5, Z:=640.0}}
        return vector3{}
    GetFocalTransform<override>()<transacts>:transform := 
        if(Card := SelectedCard?){return transform{Translation := GetCardPosition(Card) ,Scale :=vector3{X:=1.0, Y:=1.0, Z:=1.0}, Rotation := IdentityRotation()}}
        else if(Card := HandArray[0]){set SelectedCard = option{Card}; return transform{Translation := GetCardPosition(Card) ,Scale :=vector3{X:=1.0, Y:=1.0, Z:=1.0}, Rotation := IdentityRotation()}}
        return transform{}
    SelectorInteraction<override>():void:=
        if(Sol := SolitairSwitch[BackRef.GameSession],Self = Sol.CurrentPlayer(1)):
            if(Card := SelectedCard?, Play := PlayZone[BackRef.PlayArea]):
                SelectorRight()
                RemoveCard(Card)
                Play.DropHoldable(Card)
    SelectorUp<override>():void:=if(Selector := BackRef.Controller.SelectorRef?, DeckA := DeckZone[BackRef.DeckArea]){Selector.SetFocus(DeckA)}
    SelectorDown<override>():void:=if(Selector := BackRef.Controller.SelectorRef?){Selector.SetFocus(Self)}   
    SelectorRight<override>():void:=
        if(Selector := BackRef.Controller.SelectorRef?):
            if(PrevSelected := SelectedCard?,var Prev:Physical_Card = HandArray[0]):
                if(HandArray.Length > 1 and Prev <> PrevSelected):
                    for(Index := 1..HandArray.Length-1, NextCard := HandArray[Index]):
                        if(NextCard = PrevSelected):
                            set SelectedCard = option{Prev}
                            if(Index-1 <> LastIndex; Card := HandArray[Index-1]):
                                set LastIndex = option{Index-1}
                                BackRef.UpdateSelected.Signal(option{Card})
                            return Selector.SetFocus(Self)
                        else:
                            set Prev = NextCard
                set SelectedCard = option{Prev}
                if(0 <> LastIndex; Card := HandArray[0]):
                    set LastIndex = option{0}
                    BackRef.UpdateSelected.Signal(option{Card})
                return Selector.SetFocus(Self)
            else if(Index :=Floor[HandArray.Length*0.5], First := HandArray[Index]):
                set SelectedCard = option{First}
                if(Index <> LastIndex; Card := HandArray[Index]):
                    set LastIndex = option{Index}
                    BackRef.UpdateSelected.Signal(option{Card})
                return Selector.SetFocus(Self)
    SelectorLeft<override>():void:=
        if(Selector := BackRef.Controller.SelectorRef?):
            if(PrevSelected := SelectedCard?,var Prev:Physical_Card = HandArray[HandArray.Length-1]):
                if(HandArray.Length > 1 and Prev <> PrevSelected):
                    for(Index := 2..HandArray.Length, AltIndex := HandArray.Length-Index, NextCard := HandArray[AltIndex]):
                        if(NextCard = PrevSelected):
                            set SelectedCard = option{Prev}
                            if(AltIndex+1 <> LastIndex; Card := HandArray[AltIndex+1]):
                                set LastIndex = option{AltIndex+1}
                                BackRef.UpdateSelected.Signal(option{Card})
                            return Selector.SetFocus(Self)
                        else:
                            set Prev = NextCard
                set SelectedCard = option{Prev}
                if(0 <> LastIndex; Card := HandArray[0]):
                    set LastIndex = option{0}
                    BackRef.UpdateSelected.Signal(option{Card})
                return Selector.SetFocus(Self)
            else if(Index :=Floor[HandArray.Length*0.5], First := HandArray[Index]):
                set SelectedCard = option{First}
                if(Index <> LastIndex; Card := HandArray[Index]):
                    set LastIndex = option{Index}
                    BackRef.UpdateSelected.Signal(option{Card})
                return Selector.SetFocus(Self)
    CursorLeftFocus<override>(Pointer:CursorHandler):void:=
        if(Cursor := BackRef.Controller.CursorRef?):
            set LastIndex = false
            Cursor.UpdateStyle(CursorStyle.Standard)
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=return
    CursorRelease<override>(Pointer:CursorHandler, X:float, Y:float):void:=return
    GetZone<override>(X:float, Y:float):?BlankZone:=
        IsOverCard(X)
        return (super:)GetZone(X, Y)
    CursorClick<override>(Pointer:CursorHandler, X:float, Y:float):void:=
        if(Cursor := BackRef.Controller.CursorRef?, Index := Floor[(X+180.0)*0.02576489533], Card := HandArray[Index]):
            RemoveCard(Card)
            Offset := PolarToCartesian((((Index*1.0))*38.8125)-(X+160.59375),-64.58333333-Y,0.0)
            Cursor.CursorHold(option{(Card,Offset)})
    DropHoldable<override>(Obj:Grab_Obj, X:float, Y:float):void:=DropHoldable(Obj)
    DropHoldable<override>(Obj:Grab_Obj):void:=
        if(Card := Physical_Card[Obj], BZone := BlankZone[Card.HomeZone]):
            set HandArray += array{Card}
            Card.SetHome(Self)
            UpdateDeckPlacement()
    DealCard(Card:Physical_Card):void:=
        Card.SetHome(Self)
        spawn{Card.MoveToRightUp(vector3{X:=-962.0, Y:=(HandArray.Length*207.0)-851.5, Z:=640.0})}
        set HandArray += array{Card}
    ReturnCards():[]Virtual_Card:=
        Out := for(Card:HandArray){Card.Virtualise()}
        set HandArray = array{}
        return Out


AIHandZone := class(HandZone):
    XMax<override>:float := 0.0
    YMax<override>:float := 0.0
    BaseProp<override>:creative_prop := creative_prop{}
    UpdateDeckPlacement<override>():void:=if(HandArray.Length <> 0){for(Index := 0..HandArray.Length; Card := HandArray[Index]){spawn{Card.MoveToRightUp(vector3{X:=-961.0, Y:=(Index*103.5)+XMin, Z:=YMin}, vector3{X:=1.0,Y:=0.5,Z:=0.5})}}}
    RemoveHomeLink<override>(Obj:Grab_Obj):void:=if(OldCard := Physical_Card[Obj]){set HandArray = for(Card:HandArray, Card <> OldCard){Card};UpdateDeckPlacement()}
    CursorLeftFocus<override>(Pointer:CursorHandler):void:=return
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=return
    GetZone<override>(X:float, Y:float):?BlankZone:=return option{Self}
    CursorClick<override>(Pointer:CursorHandler, X:float, Y:float):void:=return
    DropHoldable<override>(Obj:Grab_Obj, X:float, Y:float):void:=DropHoldable(Obj)
    DropHoldable<override>(Obj:Grab_Obj):void:=
        if(Card := Physical_Card[Obj], BZone := BlankZone[Card.HomeZone]):
            Card.SetHome(Self)
            set HandArray += array{Card}
            UpdateDeckPlacement()
    ChooseNextCard(isTestCard:?Virtual_Card, Play:GameState):?Virtual_Card:=
        if(TestCard:=isTestCard?):
            case(Play):
                GameState.Play2orPickup2 => block{for(Card:HandArray, Card.Value = 2){return option{Card}};return false}
                GameState.Play7andDiamond => block{for(Card:HandArray, Card.Suit = CardType.Diamond){return option{Card}};return false}
                GameState.Play7andHeart => block{for(Card:HandArray, Card.Suit = CardType.Heart){return option{Card}};return false}
                GameState.Play7andSpade => block{for(Card:HandArray, Card.Suit = CardType.Spade){return option{Card}};return false}
                GameState.Play7andClub => block{for(Card:HandArray, Card.Suit = CardType.Club){return option{Card}};return false}
                GameState.Play8orSkip => block{for(Card:HandArray, Card.Value = 8){return option{Card}};return false}
                GameState.PlayKingorPickup5 => block{for(Card:HandArray, Card.Value = 13){return option{Card}};return false}
                _ => return PickStandard(TestCard)
        else:
            return false
    PickStandard(TestCard:Virtual_Card):?Virtual_Card:=
        for(Card:HandArray, TestCard.Suit = Card.Suit and TestCard.Value = 7){return option{Card}}
        for(Card:HandArray, TestCard.Suit = Card.Suit or (TestCard.Value = Card.Value <> 1)){return option{Card}}
        var Aces:[]Virtual_Card := array{}
        var SuitCounts:[CardType]int := map{CardType.Diamond => 0,CardType.Heart => 0,CardType.Spade => 0,CardType.Club => 0}
        for(Card:HandArray){if(Card.Value = 1){set Aces+=array{Card}}else{if(set SuitCounts[Card.Suit]+=1){}}}
        if(var LargestCard:Virtual_Card := Aces[0]):
            if(Aces.Length>1):
                for(Index:=1..Aces.Length, TestAce := Aces[Index], SuitCounts[TestAce.Suit]>SuitCounts[LargestCard.Suit]){set LargestCard = TestAce}
            return option{LargestCard}
        return false
    DealCard<override>(Card:Physical_Card):void:=
        Card.SetHome(Self)
        spawn{Card.MoveToRightUp(vector3{X:=-961.0, Y:=(HandArray.Length*103.5)+XMin, Z:=YMin}, vector3{X:=1.0,Y:=0.5,Z:=0.5})}
        set HandArray += array{Card}

DeckZone := class(BlankZone, Focusable):
    BackRef:CCGDemoController
    var TopCard:?Physical_Card
    var NextCard:?Physical_Card
    Deck:Card_Deck
    var hasClick:?vector2:=false
    GetFocalTransform<override>()<transacts>:transform := return transform{Translation := Deck.DeckPosition + vector3{X:=1.0} ,Scale :=vector3{X:=1.0, Y:=1.0, Z:=1.0}, Rotation := IdentityRotation()}
    SetNewDeck(Cards:[]Virtual_Card):void:=
        set TopCard = false
        set NextCard = false
        set Deck.VirtualDeck = Cards
    GetCardCount()<transacts>:int:=if(TopCard?){if(NextCard?){Deck.GetCardCount()+2}else{Deck.GetCardCount()+1}}else{Deck.GetCardCount()}
    SelectorUp<override>():void:=if(Selector := BackRef.Controller.SelectorRef?){Selector.SetFocus(Self)}  
    SelectorDown<override>():void:=
        if(Select := BackRef.Controller.SelectorRef?, DeckA := HandZone[BackRef.HandArea]):
            if(not DeckA.SelectedCard?,Index :=Floor[DeckA.HandArray.Length*0.5], First := DeckA.HandArray[Index]):
                set DeckA.SelectedCard = option{First}
                if(Index <> DeckA.LastIndex; Card := DeckA.HandArray[Index]):
                    set DeckA.LastIndex = option{Index}
                    BackRef.UpdateSelected.Signal(option{Card})
            Select.SetFocus(DeckA)
    SelectorRight<override>():void:=if(Select := BackRef.Controller.SelectorRef?, DeckA := Focusable[BackRef.GameToggle]){Select.SetFocus(DeckA)}
    SelectorInteraction<override>():void:=
        if(Sol := SolitairSwitch[BackRef.GameSession],SZone := HandZone[BackRef.HandArea], SZone = Sol.CurrentPlayer(1)){
                BackRef.GameSession.ApplyTurn(SZone,false)
            }
    CursorEnteredFocus<override>(Pointer:CursorHandler):void:=Pointer.UpdateStyle(CursorStyle.Pointing)
    CursorLeftFocus<override>(Pointer:CursorHandler):void:=Pointer.UpdateStyle(CursorStyle.Standard)
    ClickDelay()<suspends>:void:=
        Sleep(0.5)
        set hasClick = false
    CursorClick<override>(Pointer:CursorHandler, X:float, Y:float):void:=
        if(Sol := SolitairSwitch[BackRef.GameSession],SZone := HandZone[BackRef.HandArea], SZone = Sol.CurrentPlayer(1)){
            if(Sol.State = GameState.Play2orPickup2 or Sol.State = GameState.PlayKingorPickup5,Card := TopCard?, SZone.HandArray.Length<8):
                if(Click := hasClick?):
                    if(Distance(vector2{X:=X, Y:=Y},Click) < 1.5):
                        BackRef.GameSession.ApplyTurn(SZone,false)
                        return
                set hasClick = option{vector2{X:=X, Y:=Y}}
                spawn{ClickDelay()}
                Offset := PolarToCartesian(-X,24.666666667-Y,0.0)
                Pointer.CursorHold(option{(Card,Offset)}) 
            else:
                BackRef.GameSession.ApplyTurn(SZone,false)
            }
    CursorRelease<override>(Pointer:CursorHandler, X:float, Y:float):void:=return  
    RemoveHomeLink<override>(Obj:Grab_Obj):void:=
        if(Card := Physical_Card[Obj]):
            if(NCard := NextCard?):
                if(Sol := SolitairSwitch[BackRef.GameSession], SZone := BlankZone[Sol.HandAreas[0]] and Sol.PickupState > 0):#, CurrentCount := Sol.PickupCount?):
                    Sol.DecrementPickupCount()
                spawn{NCard.MoveToBackUp(Deck.DeckPosition+vector3{X:=1.0},0.05)}
                set TopCard = NextCard
                set NextCard = Deck.LoadNextCard(Self)
            else:
                if(Sol := SolitairSwitch[BackRef.GameSession], SZone := BlankZone[Sol.HandAreas[0]]):#, CurrentCount := Sol.PickupCount?):
                    Sol.RemovePickupCount()
                    Sol.ApplyTurn(SZone,false)
                set TopCard = false
    DropHoldable<override>(Obj:Grab_Obj, X:float, Y:float):void:= DropHoldable(Obj)
    DropHoldable<override>(Obj:Grab_Obj):void:=
        if(Card := Physical_Card[Obj]):
            if(Card = TopCard?):
                spawn{Card.MoveToBackUp(Deck.DeckPosition)}
            else:
                Card.ReturnHome()
    QuickShuffle(Count:int):void:=
        if(Card1 := TopCard?):
            if(Card2:=NextCard?):
                Deck.AddPhysicalCards(array{Card1,Card2})
            else:
                Deck.AddPhysicalCards(array{Card1})
        else if(Card2:=NextCard?):
            Deck.AddPhysicalCards(array{Card2})
        for(Index:=0..Count-1):
            if(DeckSlice := Floor[Deck.VirtualDeck.Length*0.5], var Deck1:[]Virtual_Card := Deck.VirtualDeck.Slice[0,DeckSlice], var Deck2:[]Virtual_Card := Deck.VirtualDeck.Slice[DeckSlice]):
                set Deck.VirtualDeck = array{}
                loop:
                    if(Deck1.Length =0 and Deck2.Length = 0):
                        break
                    else if(Deck1.Length = Deck2.Length):
                        if(GetRandomInt(0,1)=0;Select := Deck1[0], Rest := Deck1.Slice[1]):
                            set Deck.VirtualDeck += array{Select}
                            set Deck1 = Rest
                        else if(Select := Deck2[Deck2.Length-1], Rest := Deck2.Slice[0,Deck2.Length-1]):
                            set Deck.VirtualDeck += array{Select}
                            set Deck2 = Rest
                    else if(Deck1.Length > Deck2.Length, Select := Deck1[Deck1.Length-1], Rest := Deck1.Slice[0,Deck1.Length-1]):
                        set Deck.VirtualDeck += array{Select}
                        set Deck1 = Rest
                    else if(Select := Deck2[0], Rest := Deck2.Slice[1]):
                        set Deck.VirtualDeck += array{Select}
                        set Deck2 = Rest
        Card3 := Deck.LoadNextCard(Self)
        Card4 := Deck.LoadNextCard(Self)
        if(C3 := Card3?):
            C3.SetPosition(Deck.DeckPosition+vector3{X:=1.0})
            set TopCard = Card3
            if(C4:=Card4?):
                C4.SetPosition(Deck.DeckPosition)
                set NextCard = Card4
BaseZone := class(BlankZone):
    GetNextZone<override>(BaseTree:Tree,X:float, Y:float):?BlankZone:=
        return BaseTree.GetZone(X,Y)
CardType := enum:
    Diamond
    Heart
    Spade
    Club
#CardSuitToMaterial(Suit:CardType, Value:int):void:=

SuitValueToMaterial(Suit:CardType,Value:int):material:=
    case(Suit):
        CardType.Diamond => case(Value):
            1 => return MaterialsCard.AceDiamond_Mat
            2 => return MaterialsCard._2Diamond_Mat
            3 => return MaterialsCard._3Diamond_Mat
            4 => return MaterialsCard._4Diamond_Mat
            5 => return MaterialsCard._5Diamond_Mat
            6 => return MaterialsCard._6Diamond_Mat
            7 => return MaterialsCard._7Diamond_Mat
            8 => return MaterialsCard._8Diamond_Mat
            9 => return MaterialsCard._9Diamond_Mat
            10 => return MaterialsCard._10Diamond_Mat
            11 => return MaterialsCard.JDiamond_Mat
            12 => return MaterialsCard.QDiamond_Mat
            13 => return MaterialsCard.KDiamond_Mat
            _ => return MaterialsCard.CardBack_Mat
        CardType.Heart => case(Value):
            1 => return MaterialsCard.AceHeart_Mat
            2 => return MaterialsCard._2Heart_Mat
            3 => return MaterialsCard._3Heart_Mat
            4 => return MaterialsCard._4Heart_Mat
            5 => return MaterialsCard._5Heart_Mat
            6 => return MaterialsCard._6Heart_Mat
            7 => return MaterialsCard._7Heart_Mat
            8 => return MaterialsCard._8Heart_Mat
            9 => return MaterialsCard._9Heart_Mat
            10 => return MaterialsCard._10Heart_Mat
            11 => return MaterialsCard.JHeart_Mat
            12 => return MaterialsCard.QHeart_Mat
            13 => return MaterialsCard.KHeart_Mat
            _ => return MaterialsCard.CardBack_Mat
        CardType.Spade => case(Value):
            1 => return MaterialsCard.AceSpade_Mat
            2 => return MaterialsCard._2Spade_Mat
            3 => return MaterialsCard._3Spade_Mat
            4 => return MaterialsCard._4Spade_Mat
            5 => return MaterialsCard._5Spade_Mat
            6 => return MaterialsCard._6Spade_Mat
            7 => return MaterialsCard._7Spade_Mat
            8 => return MaterialsCard._8Spade_Mat
            9 => return MaterialsCard._9Spade_Mat
            10 => return MaterialsCard._10Spade_Mat
            11 => return MaterialsCard.JSpade_Mat
            12 => return MaterialsCard.QSpade_Mat
            13 => return MaterialsCard.KSpade_Mat
            _ => return MaterialsCard.CardBack_Mat
        CardType.Club => case(Value):
            1 => return MaterialsCard.AceClub_Mat
            2 => return MaterialsCard._2Club_Mat
            3 => return MaterialsCard._3Club_Mat
            4 => return MaterialsCard._4Club_Mat
            5 => return MaterialsCard._5Club_Mat
            6 => return MaterialsCard._6Club_Mat
            7 => return MaterialsCard._7Club_Mat
            8 => return MaterialsCard._8Club_Mat
            9 => return MaterialsCard._9Club_Mat
            10 => return MaterialsCard._10Club_Mat
            11 => return MaterialsCard.JClub_Mat
            12 => return MaterialsCard.QClub_Mat
            13 => return MaterialsCard.KClub_Mat
            _ => return MaterialsCard.CardBack_Mat

Grab_Obj := interface():
    SetPosition(Pos:vector3):void
    SetHome(NewHome:BlankZone):void
    ReturnHome():void

Focusable := interface():
    GetFocalTransform()<transacts>:transform

Card_Deck := class():
    FlipAudio:audio_player_device
    PlaceAudio:audio_player_device
    CardAsset:creative_prop_asset
    DeckPosition:vector3
    var VirtualDeck:[]Virtual_Card := array{}
    GetCardCount()<transacts>:int:=return VirtualDeck.Length
    AddVirtualCards(Cards:[]Virtual_Card):void:=
        set VirtualDeck += Cards
    AddPhysicalCards(Cards:[]Physical_Card):void:=
        set VirtualDeck += for(Card:Cards){Card.Virtualise()}
    LoadNextCard(SpawnZone:BlankZone):?Physical_Card:=
        if(NextCard := VirtualDeck[0], Rest := VirtualDeck.Slice[1]):
            set VirtualDeck = Rest
            return NextCard.Physicalise(DeckPosition, SpawnZone, CardAsset, FlipAudio, PlaceAudio)
        return false

Virtual_Card := class<unique>():
    Suit:CardType
    Value:int

    Physicalise(Position:vector3, Home:BlankZone,CardAsset:creative_prop_asset, FlipAudio:audio_player_device, PlaceAudio:audio_player_device):?Physical_Card :=
        isSpawned := SpawnProp(CardAsset, Position, MakeRotationFromYawPitchRollDegrees(180.0, 0.0, 0.0))
        if(Spawned := isSpawned(0)?):
            Spawned.SetMaterial(SuitValueToMaterial(Suit,Value))
            return option{Physical_Card{CardProp := Spawned,Suit := Suit, Value := Value, HomeZone := Home, FlipAudio := FlipAudio, PlaceAudio := PlaceAudio}}
        else{return false}

Physical_Card := class(Virtual_Card, Grab_Obj):
    CardProp:creative_prop
    FlipAudio:audio_player_device
    PlaceAudio:audio_player_device
    var FaceUp:logic := false
    var Rotation:rotation := MakeRotationFromYawPitchRollDegrees(180.0, 0.0, 0.0)
    var HomeZone:BlankZone
    var Visualising:logic := false
    Virtualise():Virtual_Card:=
        CardProp.Dispose()
        return Virtual_Card{Suit := Suit, Value := Value}

    IndicateEffect()<suspends>:void:=
        if(Visualising = false):
            set Visualising = true
            Pos := CardProp.GetTransform().Translation
            CardProp.MoveTo(transform{Translation:=Pos, Rotation := Rotation, Scale := vector3{X:=1.0,Y:=0.75,Z:=0.75}}, 0.1)
            CardProp.MoveTo(transform{Translation:=Pos, Rotation := Rotation, Scale := vector3{X:=1.0,Y:=0.5,Z:=0.5}}, 0.2)
            set Visualising = false

    MoveToRightUp(Pos:vector3)<suspends>:void:=
        if(FaceUp = false):
            CardProp.MoveTo(transform{Translation:=Pos-vector3{X:=207.0}, Rotation := MakeRotationFromYawPitchRollDegrees(90.0, 0.0, 0.0), Scale := vector3{X:=1.0,Y:=1.0,Z:=1.0}}, 0.5)
            set Rotation = IdentityRotation()
            FlipAudio.Play()
            CardProp.MoveTo(transform{Translation:=Pos, Rotation := Rotation, Scale := vector3{X:=1.0,Y:=1.0,Z:=1.0}}, 0.25)
            set FaceUp = true
        else:
            PlaceAudio.Play()
            CardProp.MoveTo(Pos,Rotation, 0.75)
    MoveToRightUp(Pos:vector3,Scale:vector3)<suspends>:void:=
        if(FaceUp = false):
            CardProp.MoveTo(transform{Translation:=Pos-vector3{X:=207.0}, Rotation := MakeRotationFromYawPitchRollDegrees(90.0, 0.0, 0.0), Scale := Scale}, 0.5)
            set Rotation = IdentityRotation()
            FlipAudio.Play()
            CardProp.MoveTo(transform{Translation:=Pos,Rotation := Rotation,Scale := Scale}, 0.25)
            set FaceUp = true
        else:
            PlaceAudio.Play()
            CardProp.MoveTo(transform{Translation:=Pos,Rotation := Rotation,Scale := Scale}, 0.75)

    MoveToBackUp(Pos:vector3)<suspends>:void:=
        
        if(FaceUp = true):
            CardProp.MoveTo(Pos-vector3{X:=207.0},MakeRotationFromYawPitchRollDegrees(90.0, 0.0, 0.0), 0.5)
            set Rotation = MakeRotationFromYawPitchRollDegrees(180.0, 0.0, 0.0)
            FlipAudio.Play()
            CardProp.MoveTo(Pos,Rotation, 0.25)           
            set FaceUp = false
        else:
            PlaceAudio.Play()
            CardProp.MoveTo(Pos,Rotation, 0.75)

    MoveToBackUp(Pos:vector3, Time:float)<suspends>:void:=
        CardProp.MoveTo(Pos,Rotation, Time)
        if(FaceUp = true):
            HalfTime := Time*0.5
            CardProp.MoveTo(Pos-vector3{X:=207.0},MakeRotationFromYawPitchRollDegrees(90.0, 0.0, 0.0), HalfTime)
            set Rotation = MakeRotationFromYawPitchRollDegrees(180.0, 0.0, 0.0)
            FlipAudio.Play()
            CardProp.MoveTo(Pos,Rotation, HalfTime)  
            set FaceUp = false  
        else:
            PlaceAudio.Play()
            CardProp.MoveTo(Pos,Rotation, Time)

    SetPosition<override>(Pos:vector3):void:=if(CardProp.TeleportTo[Pos,Rotation]){}
    ReturnHome<override>():void:=
        HomeZone.DropHoldable(Self)
    SetHome<override>(NewHome:BlankZone):void:=
        HomeZone.RemoveHomeLink(Self)
        set HomeZone = NewHome
Node := class(Tree):
    X:float
    Y:float
    PP:Tree
    PN:Tree
    NP:Tree
    NN:Tree

    GetZone<override>(InX:float,InY:float):?BlankZone:=
        if(InX>=X):
            if(InY>=Y):
                return PP.GetZone(InX, InY)
            else:
                return PN.GetZone(InX, InY)
        else:
            if(InY>=Y):
                return NP.GetZone(InX, InY)
            else:
                return NN.GetZone(InX, InY)

CursorState := enum:
    Passive
    Active

CursorStyle := enum:
    Standard
    Pointing
    Holding
    Denial

StyleToMaterial(Style:CursorStyle):material:=
    case(Style):
        CursorStyle.Standard => return MaterialsCursor.StandardCursor
        CursorStyle.Pointing => return MaterialsCursor.PointingCursor
        CursorStyle.Holding => return MaterialsCursor.HoldingCursor
        CursorStyle.Denial => return MaterialsCursor.DenialCursor

DualController := class():
    var ZoneTree:Node

    var CursorRef:?CursorHandler := false
    var SelectorRef:?SelectorHandler := false
    
    var CursorDepth:float := 980.0
    var CursorInUse:logic := false
    var Interacting:logic := false
    var LastCursorPos:vector2 := vector2{}

    UpdateCharAngles(RotYaw:float,RotPitch:float):void:=
        if(Cursor := CursorRef?):
            if(CursorInUse = false):
                NewPos := vector2{X:=RotYaw, Y:=RotPitch}
                if(Distance(LastCursorPos,NewPos)>=2.0):
                    SetState(true)
                    Cursor.UpdateCursor(ZoneTree, RotYaw, RotPitch)
                set LastCursorPos = NewPos
            else:
                Cursor.UpdateCursor(ZoneTree, RotYaw, RotPitch)
        
    SetState(State:logic):logic:=
        if(not CursorRef?.isHolding?;Selector := SelectorRef?; Cursor := CursorRef?):
            set CursorInUse = State
            if(State = true){Cursor.CursorProp.Show();Cursor.CurrentZone.CursorEnteredFocus(Cursor);Selector.BaseProp.Hide()}else{Cursor.CursorProp.Hide();Cursor.CurrentZone.CursorLeftFocus(Cursor);Selector.BaseProp.Show()}
            return true
        return false

    PressInteract(Agent:agent):void:=
        if(Cursor := CursorRef?, Selector := SelectorRef?, FortChar := Agent.GetFortCharacter[]):
            if(CursorInUse = true, Rot := FortChar.GetViewRotation().GetYawPitchRollDegrees(),RotYaw := Rot[0], RotPitch := Rot[1]):
                Cursor.CursorClick(ZoneTree,RotYaw,RotPitch)
            else if(Selector.InteractionDelayed = false and Selector.Lock = false):
                spawn{Selector.InteractionDelay()}
                Selector.SelectedZone.SelectorInteraction()
                

    ReleaseInteract(Agent:agent, Time:float):void:=
        if(Cursor := CursorRef?, FortChar := Agent.GetFortCharacter[]):
            if(CursorInUse = true, Rot := FortChar.GetViewRotation().GetYawPitchRollDegrees(),RotYaw := Rot[0], RotPitch := Rot[1]):
                Cursor.CursorRelease(ZoneTree,RotYaw,RotPitch)


SelectorHandler := class():
    BaseProp:creative_prop
    BackRef:DualController
    var SelectedZone:BlankZone
    var InteractionDelayed:logic := false
    var Lock:logic := false

    InteractionDelay()<suspends>:void:=
        set InteractionDelayed = true
        Sleep(1.0)
        set InteractionDelayed = false

    SetFocus(Obj:Focusable):void:=
        if(Zone:=BlankZone[Obj],BaseProp.TeleportTo[Obj.GetFocalTransform()]){set SelectedZone = Zone}
        

    SelectorUp(Agent:agent):void:=
        if(BackRef.CursorInUse = true):
            NewState := BackRef.SetState(false)
            if(NewState=true):
                SelectedZone.SelectorUp()
        else:
            SelectedZone.SelectorUp()
    SelectorDown(Agent:agent):void:=
        if(BackRef.CursorInUse = true):
            NewState := BackRef.SetState(false)
            if(NewState=true):
                SelectedZone.SelectorDown()
        else:
            SelectedZone.SelectorDown()
    SelectorLeft(Agent:agent):void:=
        if(BackRef.CursorInUse = true):
            NewState := BackRef.SetState(false)
            if(NewState=true):
                SelectedZone.SelectorLeft()
        else:
            SelectedZone.SelectorLeft()
    SelectorRight(Agent:agent):void:=
        if(BackRef.CursorInUse = true):
            NewState := BackRef.SetState(false)
            if(NewState=true):
                SelectedZone.SelectorRight()
        else:
            SelectedZone.SelectorRight()

CursorHandler := class():
    BackRef:DualController
    CursorProp:creative_prop

    var isHolding:?tuple(Grab_Obj,vector3) := false
    var CurrentZone:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var State:CursorState := CursorState.Passive
    var Style:CursorStyle := CursorStyle.Standard

    CursorHold(Holdable:?tuple(Grab_Obj,vector3)):void:=if(not isHolding?){BackRef.SetState(true);set isHolding = Holdable; UpdateStyle(CursorStyle.Holding);set State = CursorState.Active; UpdateStyle(CursorStyle.Standard)}

    CursorClick(SelectedTree:Tree, X:float, Y:float):void:=
        UpdateCursor(SelectedTree, X, Y)
        CurrentZone.CursorClick(Self, X, Y)

    CursorRelease(SelectedTree:Tree, X:float, Y:float):void:=
        if(Holding := isHolding?):
            set isHolding = false
            UpdateCursor(SelectedTree, X, Y)
            CurrentZone.DropHoldable(Holding(0), X, Y)
            set State = CursorState.Passive
            UpdateStyle(Style)
        CurrentZone.CursorRelease(Self, X, Y)

    UpdateStyle(DesiredStyle:CursorStyle):void:=
        if(State = CursorState.Passive):
            CursorProp.SetMaterial(StyleToMaterial(DesiredStyle))
            set Style = DesiredStyle
        else:
            set Style = DesiredStyle

    UpdateCursor(SelectedTree:Tree, X:float, Y:float):void:=
        if(BackRef.CursorDepth >0.0):
            Position := PolarToCartesianWorld(-X,Y, BackRef.CursorDepth)
            if(CursorProp.TeleportTo[Position, MakeRotationFromYawPitchRollDegrees(180.0,0.0,0.0)]):
                if(Holding := isHolding?){Holding(0).SetPosition(Position+Holding(1))}
                isNewZone := SelectedTree.GetZone(X,Y)
                if(NewZone := isNewZone?, NewZone <> CurrentZone):
                    CurrentZone.CursorLeftFocus(Self)
                    NewZone.CursorEnteredFocus(Self)
                    set CurrentZone = NewZone
        else:
            Position := PolarToCartesianWorld(X,Y, BackRef.CursorDepth)
            if(CursorProp.TeleportTo[Position, IdentityRotation()]):
                if(Holding := isHolding?){Holding(0).SetPosition(Position+Holding(1))}
                isNewZone := SelectedTree.GetZone(X,Y)
                if(NewZone := isNewZone?, NewZone <> CurrentZone):
                    CurrentZone.CursorLeftFocus(Self)
                    NewZone.CursorEnteredFocus(Self)
                    set CurrentZone = NewZone

CCGDemoController := class(creative_device):
    @editable
    FlipAudio:audio_player_device := audio_player_device{}
    @editable
    PlaceAudio:audio_player_device := audio_player_device{}
    @editable
    CardProp:creative_prop_asset := DefaultCreativePropAsset
    @editable
    CursorClickTrigger:input_trigger_device := input_trigger_device{}
    @editable
    CameraSwivle:creative_prop := creative_prop{}
    @editable
    CursorProp:creative_prop := creative_prop{}
    @editable
    SelectorProp:creative_prop := creative_prop{}
    @editable
    HandBaseProp:creative_prop := creative_prop{}
    @editable
    PlayBaseProp:creative_prop := creative_prop{}
    @editable
    PlayerRef:player_reference_device := player_reference_device{}

    @editable
    GameButton:creative_prop := creative_prop{}
    @editable
    InfoButton:creative_prop := creative_prop{}

    @editable
    SelectorInteractTrigger:input_trigger_device := input_trigger_device{}
    @editable
    TriggerUp:input_trigger_device := input_trigger_device{}
    @editable
    TriggerDown:input_trigger_device := input_trigger_device{}
    @editable
    TriggerLeft:input_trigger_device := input_trigger_device{}
    @editable
    TriggerRight:input_trigger_device := input_trigger_device{}

    @editable
    SkipText:billboard_device := billboard_device{}
    @editable
    Pickup1Text:billboard_device := billboard_device{}
    @editable
    Pickup2Text:billboard_device := billboard_device{}
    @editable
    Pickup3Text:billboard_device := billboard_device{}
    @editable
    Pickup4Text:billboard_device := billboard_device{}
    @editable
    Pickup5Text:billboard_device := billboard_device{}

    @editable
    Rule2Text:billboard_device := billboard_device{}
    @editable
    Rule7Text:billboard_device := billboard_device{}
    @editable
    Rule8Text:billboard_device := billboard_device{}
    @editable
    Rule11Text:billboard_device := billboard_device{}
    @editable
    Rule13Text:billboard_device := billboard_device{}

    @editable
    Player1Text:billboard_device := billboard_device{}
    @editable
    Player2Text:billboard_device := billboard_device{}
    @editable
    Player3Text:billboard_device := billboard_device{}
    @editable
    Player4Text:billboard_device := billboard_device{}

    @editable
    LastWinText:billboard_device := billboard_device{}
    @editable
    LastLoseText:billboard_device := billboard_device{}

    @editable
    WinMessage:hud_message_device := hud_message_device{}
    @editable
    LoseMessage:hud_message_device := hud_message_device{}

    @editable
    Portal1:matchmaking_portal_device := matchmaking_portal_device{}
    @editable
    Portal1Frame:creative_prop := creative_prop{}
    @editable
    Portal2:matchmaking_portal_device := matchmaking_portal_device{}
    @editable
    Portal2Frame:creative_prop := creative_prop{}
    @editable
    ShowInteract:input_trigger_device := input_trigger_device{}
    @editable
    HideInteract:input_trigger_device := input_trigger_device{}


    UpdateSelected:event(?Physical_Card) := event(?Physical_Card){}
    var GameTree:Node := Node{X:=0.0, Y:=39.166666666, PP := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}, PN := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0} , NP := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}, NN := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}}
    var InfoTree:Node := Node{X:=0.0, Y:=39.166666666, PP := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}, PN := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0} , NP := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}, NN := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}}
    var ToggleButtonArea:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var HandArea:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var DeckArea:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var PlayArea:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var LevelArea1:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var LevelArea2:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var GameToggle:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var InfoToggle:BlankZone := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}
    var GameSession:Game := EmptyGame{}
    var RefPlayer:?player := false
    var Controller:DualController := DualController{
        ZoneTree:=Node{X:=0.0, Y:=39.166666666, PP := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}, PN := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0} , NP := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}, NN := BlankZone{XMin := 0.0, XMax := 0.0, YMin := 0.0, YMax := 0.0}}
            }
    NoZone:BaseZone := BaseZone{
        XMin:= -180.0
        XMax := 180.0
        YMin := -90.0
        YMax :=90.0
        }

    OnBegin<override>()<suspends>:void=
        CursorProp.Hide()
        Portal1.Disable()
        Portal2.Disable()
        SkipText.HideText()
        Pickup1Text.HideText()
        Pickup2Text.HideText()
        Pickup3Text.HideText()
        Pickup4Text.HideText()
        Pickup5Text.HideText()
        Rule2Text.HideText()
        Rule7Text.HideText()
        Rule8Text.HideText()
        Rule11Text.HideText()
        Rule13Text.HideText()
        Player1Text.HideText()
        Player2Text.HideText()
        Player3Text.HideText()
        Player4Text.HideText()
        LastWinText.HideText()
        LastLoseText.HideText()
        var CardsList:[]Virtual_Card := array{}
        for(Suit:array{CardType.Diamond,CardType.Heart,CardType.Spade,CardType.Club}){set CardsList += for(Index:=1..13){Virtual_Card{Suit:=Suit, Value := Index}}}
        CardDeck := Card_Deck{CardAsset := CardProp, FlipAudio := FlipAudio, PlaceAudio := PlaceAudio, DeckPosition := vector3{X:= -964.0,Y:=0.0, Z:=1172.0 },VirtualDeck := CardsList}
        set PlayArea = PlayZone{
            BackRef := Self
            BaseProp := PlayBaseProp
            XMin := 17.632653-126.5625
            XMax := 17.632653-48.0
            YMin := -24.75
            YMax := 52.6666666666667
            }
        set HandArea = HandZone{
            BackRef := Self
            BaseProp := HandBaseProp
            XMin := -180.0
            XMax := 132.0
            YMin := -90.0
            YMax :=-39.166666666
            }
        Level1 := LevelZone{
            Frame := Portal1Frame
            PortalLink := Portal1
            PictureMat :=Materials.SquareKoM_Mat
            VideoMat := Materials.KoMFlipBook
            Pos := vector3{X:= 961.0,Y := 720.0,Z :=754.0}
            BackRef := Self
            XMin := 90.0
            XMax := 180.0
            YMin := -90.0
            YMax := -39.166666666
        }
        Level2 := LevelZone{
            Frame := Portal2Frame
            PortalLink := Portal2
            PictureMat :=Materials.SquareMainImage_Mat
            VideoMat := Materials.SHGGFlipBook
            Pos := vector3{X:= 961.0,Y := 240.0,Z :=754.0}
            BackRef := Self
            XMin := 0.0
            XMax := 90.0
            YMin := -90.0
            YMax := -39.166666666
        }

        NewDeckZone :=DeckZone{
            BackRef := Self
            TopCard := false
            NextCard := false
            Deck :=CardDeck
            XMin := -19.40625
            XMax := 19.40625
            YMin := 0.0
            YMax :=49.5
            }
        set LevelArea1 = Level1
        set LevelArea2 = Level2
        set GameToggle = GameToggleButtonZone{
            LeftLink := NewDeckZone
            ButtonPos := vector3{X:= -960.0,Y := -768.0,Z :=928.0}
            BackRef := Self
            BaseProp := GameButton
            XMin := -180.0
            XMax := -108.929847
            YMin := -39.166666666
            YMax :=8.33333333333-39.166666666
            }
        ITog := InfoToggleButtonZone{
            ButtonPos := vector3{X:= 964.0,Y := -696.0,Z :=840.0}
            LeftLink := LevelArea2
            BackRef := Self
            BaseProp := InfoButton
            XMin := 90.0
            XMax := 180.0
            YMin := -39.166666666
            YMax := -22.33333333333
            }
        set Level2.LeftLink = ITog
        set Level2.RightLink = LevelArea1
        set Level1.LeftLink = LevelArea2
        set InfoToggle = ITog
        FirstCard := CardDeck.LoadNextCard(NewDeckZone)
        set NewDeckZone.TopCard = FirstCard
        SecondCard := CardDeck.LoadNextCard(NewDeckZone)
        set NewDeckZone.NextCard = SecondCard
        set DeckArea = NewDeckZone 
        set GameTree = Node{X:=-19.40625,Y:=-39.166666666,PP := Node{X:=-19.40625,Y:=0.0,PP := Node{X:=19.40625, Y:=49.5, PP := NoZone, PN := NoZone, NP := NoZone, NN := DeckArea},PN := NoZone,NP := NoZone,NN := NoZone},PN := Node{X:=132.0, Y:=-39.166666666, PP := NoZone, PN := NoZone, NP := HandArea, NN := HandArea},NP := Node{X:=17.632653-48.0,Y:=-24.75,PP := NoZone,PN := NoZone,NP := Node{X:=17.632653-126.5625, Y:=52.6666666666667, PP := NoZone, PN := PlayArea, NP := NoZone, NN := Node{X:=-108.929847, Y:=(8.33333333333*2.0)-24.75, PP := NoZone, PN := GameToggle, NP := NoZone, NN := GameToggle}},NN := NoZone},NN := Node{X:=-180.0, Y:=-39.166666666, PP := HandArea, PN := HandArea, NP := NoZone, NN := NoZone}}
        
        
        set InfoTree = Node{X:=90.0, Y:=-22.33333333333, PP := NoZone, PN := Node{X:=180.0,Y:=-39.166666666, PP := InfoToggle, PN := NoZone, NP := InfoToggle, NN := NoZone} , NP := NoZone, NN := Node{X:=0.0, Y:=-67.5, PP := NoZone, PN := NoZone, NP := Node{X:=-90.0, Y:=67.5, PP := Level2, PN := Level2, NP := Level1, NN := Level1}, NN := NoZone}}
        NewController := DualController{ZoneTree:=InfoTree}
        Selector := SelectorHandler{BackRef := NewController, BaseProp := SelectorProp, SelectedZone := DeckArea}
        set Controller = NewController
        set Controller.CursorRef = option{CursorHandler{BackRef := NewController, CursorProp := CursorProp}}
        set Controller.SelectorRef = option{Selector}
        Selector.SetFocus(ITog)
        loop:
            Agent := PlayerRef.GetAgent()
            if(Char := Agent?.GetFortCharacter[], Deck := DeckZone[DeckArea], Play := PlayZone[PlayArea], Hand := HandZone[HandArea]):
                if(PRef := player[Agent?]){set RefPlayer = option{PRef}}
                set GameSession = SolitairSwitch{
                    BackRef := Self
                    SkipText:=SkipText
                    Pickup1Text:=Pickup1Text
                    Pickup2Text:=Pickup2Text
                    Pickup3Text:=Pickup3Text
                    Pickup4Text:=Pickup4Text
                    Pickup5Text:=Pickup5Text
                    Rule2Text:=Rule2Text
                    Rule7Text:=Rule7Text
                    Rule8Text:=Rule8Text
                    Rule11Text:=Rule11Text
                    Rule13Text:=Rule13Text
                    LastWinText := LastWinText
                    LastLoseText := LastLoseText
                    PlayerText := array{Player1Text,Player2Text,Player3Text,Player4Text}

                    DeckArea := Deck
                    PlayArea := Play
                    HandAreas := array{Hand,AIHandZone{BackRef := Self,XMin := 181.75, YMin := 928.0},AIHandZone{BackRef := Self,XMin := 181.75, YMin := 1120.0},AIHandZone{BackRef := Self, XMin := 181.75, YMin := 1312.0}}
                    CurrentPlayer := (0,Hand)
                    }
                CursorClickTrigger.PressedEvent.Subscribe(Controller.PressInteract)
                CursorClickTrigger.ReleasedEvent.Subscribe(Controller.ReleaseInteract)
                SelectorInteractTrigger.PressedEvent.Subscribe(Controller.PressInteract)
                TriggerUp.PressedEvent.Subscribe(Selector.SelectorUp)
                TriggerDown.PressedEvent.Subscribe(Selector.SelectorDown)
                TriggerLeft.PressedEvent.Subscribe(Selector.SelectorLeft)
                TriggerRight.PressedEvent.Subscribe(Selector.SelectorRight)
                race:
                    GameSession.HandleGame()
                    loop:
                        Rot := Char.GetViewRotation().GetYawPitchRollDegrees()
                        if(RotYaw := Rot[0], RotPitch := Rot[1]){Controller.UpdateCharAngles(RotYaw,RotPitch)}
                        Sleep(0.0)
            Sleep(0.0)

    var InfoShowing:?logic := option{true}
    ToggleInfoState():void:=
        if(Showing := InfoShowing?):
            if(Showing = true):
                if(Focus := Focusable[DeckArea], Sel := Controller.SelectorRef?){Sel.SetFocus(Focus)}
                set Controller.CursorDepth = -980.0
                set InfoShowing = false
                set Controller.ZoneTree = GameTree
                spawn{SwivleCamera(false)}
            else:
                if(Focus := Focusable[InfoToggle], Sel := Controller.SelectorRef?){Sel.SetFocus(Focus)}
                set Controller.CursorDepth = 980.0
                set InfoShowing = false
                set Controller.ZoneTree = InfoTree
                spawn{SwivleCamera(true)}

    SwivleCamera(State:logic)<suspends>:void:=
        if(SGame := SolitairSwitch[GameSession]):
            if(State = false):
                CameraSwivle.MoveTo(vector3{}, MakeRotationFromYawPitchRollDegrees(0.0,0.0,0.0),1.0)
                set SGame.Paused = false
                set InfoShowing = option{false}
            else:
                set SGame.Paused = true
                CameraSwivle.MoveTo(vector3{}, MakeRotationFromYawPitchRollDegrees(180.0,0.0,0.0),1.0)
                set InfoShowing = option{true}

    Visualise(Visualiser:TurnVitualiser)<suspends>:?Virtual_Card:=
        var Current:?Physical_Card := false
        loop:
            set Current = race:
                UpdateSelected.Await()
                block:
                    if(Array := Visualiser.SimulatedCards[Virtual_Card[Current?]]):
                        VisLoop(Array)
                    else:
                        UpdateSelected.Await()
        return false

    VisLoop(Array:[]Virtual_Card)<suspends>:?Physical_Card :=
        loop:
            for(VirtCard:Array;Card := Physical_Card[VirtCard]):
                spawn{Card.IndicateEffect()}
                Sleep(0.5)
            Sleep(0.25)
        return false

GameState := enum:
    StandardTurn
    Play2orPickup2
    Play7andDiamond
    Play7andHeart
    Play7andSpade
    Play7andClub
    Play8orSkip
    PlayKingorPickup5

ToString(State:GameState):string:=
    case(State):
        GameState.StandardTurn => return "StandardTurn"
        GameState.Play2orPickup2 => return "Play2orPickup2"
        GameState.Play7andDiamond => return "Play7andDiamond"
        GameState.Play7andHeart => return "Play7andHeart"
        GameState.Play7andSpade => return "Play7andSpade"
        GameState.Play7andClub => return "Play7andClub"
        GameState.Play8orSkip => return "Play8orSkip"
        GameState.PlayKingorPickup5 => return "PlayKingorPickup5"
BoolToString(Val:logic):string:=
    if(Val=true){return "True"}else{return "False"}
Game:=interface:
    var GamePaused:logic:=false
    OutOfCards():void
    DealCards(Count:int)<suspends>:void
    HandleGame()<suspends>:void
    CanPlay(Array:[]Virtual_Card):logic
    ApplyTurn(Player:BlankZone,isCard:?Virtual_Card):logic
EmptyGame := class(Game):
    OutOfCards<override>():void:=return
    DealCards<override>(Count:int)<suspends>:void:=return
    HandleGame<override>()<suspends>:void:=return
    ApplyTurn<override>(Player:BlankZone,isCard:?Virtual_Card):logic:=return false
    CanPlay<override>(Array:[]Virtual_Card):logic:=return false
SolitairSwitch := class(EmptyGame):
    BackRef:CCGDemoController
    ChooseCard:event(?Virtual_Card):=event(?Virtual_Card){}
    DeckArea:DeckZone
    PlayArea:PlayZone
    HandAreas:[]HandZone
    EndGame:event(logic):=event(logic){}

    var CurrentPlayer:tuple(int, HandZone)
    var LastPlayedCard:?Virtual_Card := false
    var State:GameState := GameState.StandardTurn
    var Reversed:logic := false
    var Paused:logic := true

    var GameString:string := ""

    SkipText:billboard_device
    Pickup1Text:billboard_device
    Pickup2Text:billboard_device
    Pickup3Text:billboard_device
    Pickup4Text:billboard_device
    Pickup5Text:billboard_device
    Rule2Text:billboard_device
    Rule7Text:billboard_device
    Rule8Text:billboard_device
    Rule11Text:billboard_device
    Rule13Text:billboard_device
    LastWinText:billboard_device
    LastLoseText:billboard_device
    PlayerText:[]billboard_device

    
    OutOfCards<override>():void:=
        if(TopCard := PlayArea.PhysicalStack[PlayArea.PhysicalStack.Length-1], Rest := PlayArea.PhysicalStack.Slice[0,PlayArea.PhysicalStack.Length-1]):
            DeckArea.Deck.AddVirtualCards(PlayArea.VirtualStack+for(RCard:Rest){RCard.Virtualise()})
            set PlayArea.PhysicalStack = array{TopCard}
            set PlayArea.VirtualStack = array{}

    DealCards<override>(Count:int)<suspends>:void:=
        for(Index:=0..Count-1):
            for(HandIndex := 0..3,Hand:=HandAreas[HandIndex], Card := DeckArea.TopCard?):
                Hand.DealCard(Card)
                Sleep(0.2)
    
    ApplyTurn<override>(Player:BlankZone, isCard:?Virtual_Card):logic:=
        if(Physical_Card[isCard?].HomeZone = DeckArea):
            ChooseCard.Signal(isCard)
            return true
        else if(Player = CurrentPlayer(1)):
            if(OldCard := LastPlayedCard?, Card := isCard?):
                case(State):
                    GameState.Play2orPickup2 => if(Card.Value <> 2){return false}
                    GameState.Play7andDiamond => if(Card.Suit <> CardType.Diamond){return false}
                    GameState.Play7andHeart => if(Card.Suit <> CardType.Heart){return false}
                    GameState.Play7andSpade => if(Card.Suit <> CardType.Spade){return false}
                    GameState.Play7andClub => if(Card.Suit <> CardType.Club){return false}
                    GameState.Play8orSkip => if(Card.Value <> 8){return false}
                    GameState.PlayKingorPickup5 => if(Card.Value <> 13){return false}
                    _ => if(Card.Suit <> OldCard.Suit and Card.Value <> OldCard.Value and Card.Value <> 1){return false}
                ChooseCard.Signal(isCard)
                return true
            else:
                ChooseCard.Signal(isCard)
                return true
        else:
            return false

    NextPlayer():void:=
        if(Reversed = false):
            if(CurrentPlayer(0)<3, NextPlayerID:=CurrentPlayer(0)+1, Player := HandAreas[NextPlayerID]):
                if(OldText := PlayerText[CurrentPlayer(0)], NewText := PlayerText[NextPlayerID]):
                    OldText.HideText()
                    NewText.ShowText()
                set CurrentPlayer = (NextPlayerID,Player)
            else if(Player := HandAreas[0]):
                if(OldText := PlayerText[CurrentPlayer(0)], NewText := PlayerText[0]):
                    OldText.HideText()
                    NewText.ShowText()
                set CurrentPlayer = (0,Player)
        else:
            if(CurrentPlayer(0)>0, NextPlayerID:=CurrentPlayer(0)-1, Player := HandAreas[NextPlayerID]):
                if(OldText := PlayerText[CurrentPlayer(0)], NewText := PlayerText[NextPlayerID]):
                    OldText.HideText()
                    NewText.ShowText()
                set CurrentPlayer = (NextPlayerID,Player)
            else if(Player := HandAreas[3]):
                if(OldText := PlayerText[CurrentPlayer(0)], NewText := PlayerText[3]):
                    OldText.HideText()
                    NewText.ShowText()
                set CurrentPlayer = (3,Player)

    Play2orPickup2(isCard:?Virtual_Card)<suspends>:void:=
        if(Card := isCard?):
            if(PhyCard := Physical_Card[Card]){PlayArea.DropHoldable(PhyCard)}
            SetState(GameState.Play2orPickup2)
        else:
            if(PickupState = -1):
                set PickupState = 0
            else:
                AddCardsToPlayer(2)
            SetState(GameState.StandardTurn)
        NextPlayer()


    Play7andSuit(Suit:CardType, isCard:?Virtual_Card):void:=
        if(Card := isCard?, Card.Suit = Suit):
            set LastPlayedCard = isCard
            if(PhyCard := Physical_Card[Card]){PlayArea.DropHoldable(PhyCard)}
        else:
            SetState(GameState.StandardTurn)
            NextPlayer()

    Play8orSkip(isCard:?Virtual_Card):void:=
        if(Card := isCard?):
            set LastPlayedCard = isCard
            if(PhyCard := Physical_Card[Card]){PlayArea.DropHoldable(PhyCard)}
        else:
            SetState(GameState.StandardTurn)
        NextPlayer()

    PlayKingorPickup5(isCard:?Virtual_Card)<suspends>:void:=
        if(Card := isCard?):
            set LastPlayedCard = isCard
            if(Card.Suit = CardType.Diamond or Card.Suit = CardType.Heart):
                SetState(GameState.StandardTurn)
            else:
                SetState(GameState.PlayKingorPickup5)
            if(PhyCard := Physical_Card[Card]){PlayArea.DropHoldable(PhyCard)}

        else:
            if(PickupState = -1):
                set PickupState = 0
            else:
                AddCardsToPlayer(5)
            SetState(GameState.StandardTurn)
        NextPlayer()

    StandardPlay(isCard:?Virtual_Card):void:=
        if(Card := isCard?):
            case(Card.Value):
                2 => SetState(GameState.Play2orPickup2)
                7 => block:
                    case(Card.Suit):
                        CardType.Diamond => SetState(GameState.Play7andDiamond)
                        CardType.Heart => SetState(GameState.Play7andHeart)
                        CardType.Spade => SetState(GameState.Play7andSpade)
                        CardType.Club => SetState(GameState.Play7andClub)
                    return if(PhyCard := Physical_Card[Card]){PlayArea.DropHoldable(PhyCard)} 
                8 => SetState(GameState.Play8orSkip)
                11 => if(Reversed = true){set Reversed = false; Rule11Text.HideText()}else{set Reversed = true; Rule11Text.ShowText()}
                13 => if(Card.Suit = CardType.Spade or Card.Suit = CardType.Club){SetState(GameState.PlayKingorPickup5)}
                _ =>
            if(PhyCard := Physical_Card[Card]){PlayArea.DropHoldable(PhyCard)}
        NextPlayer()

    AddCardsToPlayer(Count:int)<suspends>:void:=
        for(Index:=0..Count-1;NewCard:=DeckArea.TopCard?;CurrentPlayer(1).HandArray.Length < 8){
            CurrentPlayer(1).DropHoldable(NewCard)
            Sleep(1.1)
        }
    CanPlay<override>(Array:[]Virtual_Card):logic:=
        if(LastCard := LastPlayedCard?):
            for(Card:Array, Card.Suit = LastCard.Suit or Card.Value = LastCard.Value){
                return true}
            return false
        return true
    var PickupState:int :=0
    GetPickupCountText(Count:int)<transacts>:billboard_device:=
        PickCount := Clamp(Clamp(Count,0,8-CurrentPlayer(1).HandArray.Length),0,DeckArea.GetCardCount())
        case(PickCount):
            5 => block{set PickupState = 5; return Pickup5Text}
            4 => block{set PickupState = 4; return Pickup4Text}
            3 => block{set PickupState = 3; return Pickup3Text}
            2 => block{set PickupState = 2; return Pickup2Text}
            1 => block{set PickupState = 1; return Pickup1Text}
            _ => block{set PickupState = 0; return billboard_device{}}

    RemovePickupCount():void:=
        Rule2Text.HideText()
        Rule13Text.HideText()
        case(PickupState):
            5 => Pickup5Text.HideText()
            4 => Pickup4Text.HideText()
            3 => Pickup3Text.HideText()
            2 => Pickup2Text.HideText()
            1 => Pickup1Text.HideText()
            _ => SkipText.HideText()
        set PickupState = 0

    DecrementPickupCount():void:=
        case(PickupState):
            5 => block{Pickup5Text.HideText(),Pickup4Text.ShowText(),set PickupState = 4}
            4 => block{Pickup4Text.HideText(),Pickup3Text.ShowText(),set PickupState = 3}
            3 => block{Pickup3Text.HideText(),Pickup2Text.ShowText(),set PickupState = 2}
            2 => block{Pickup2Text.HideText(),Pickup1Text.ShowText(),set PickupState = 1}
            _ => block{Pickup1Text.HideText(),RemovePickupCount();set PickupState = -1;ApplyTurn(CurrentPlayer(1),false)}

    ClampedPickup(Val:int, Hand:HandZone)<transacts>:int := Clamp(Clamp(Val,0,8-Hand.HandArray.Length),0,DeckArea.GetCardCount())
    SetState(NewState:GameState):void:=
        case(State):
            GameState.StandardTurn => case(NewState):
                GameState.StandardTurn => 
                GameState.Play2orPickup2 => block{GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => block{Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andHeart => block{Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andSpade => block{Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andClub => block{Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play8orSkip => block{Rule8Text.ShowText(),SkipText.ShowText()}
                GameState.PlayKingorPickup5 => block{GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.Play2orPickup2 => case(NewState):
                GameState.StandardTurn => RemovePickupCount()
                GameState.Play2orPickup2 => block{RemovePickupCount(),GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andHeart => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andSpade => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andClub => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play8orSkip => block{RemovePickupCount(),Rule8Text.ShowText(),SkipText.ShowText()}
                GameState.PlayKingorPickup5 => block{RemovePickupCount();GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.Play7andDiamond => case(NewState):
                GameState.StandardTurn => block{Rule7Text.HideText(),SkipText.HideText()}
                GameState.Play2orPickup2 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => 
                GameState.Play7andHeart => 
                GameState.Play7andSpade => 
                GameState.Play7andClub => 
                GameState.Play8orSkip => block{Rule7Text.HideText(),SkipText.HideText(),Rule8Text.ShowText()}
                GameState.PlayKingorPickup5 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.Play7andHeart => case(NewState):
                GameState.StandardTurn => block{Rule7Text.HideText(),SkipText.HideText()}
                GameState.Play2orPickup2 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => 
                GameState.Play7andHeart => 
                GameState.Play7andSpade => 
                GameState.Play7andClub => 
                GameState.Play8orSkip => block{Rule7Text.HideText(),SkipText.HideText(),Rule8Text.ShowText()}
                GameState.PlayKingorPickup5 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.Play7andSpade => case(NewState):
                GameState.StandardTurn => block{Rule7Text.HideText(),SkipText.HideText()}
                GameState.Play2orPickup2 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => 
                GameState.Play7andHeart =>
                GameState.Play7andSpade => 
                GameState.Play7andClub => 
                GameState.Play8orSkip => block{Rule7Text.HideText(),SkipText.HideText(),Rule8Text.ShowText()}
                GameState.PlayKingorPickup5 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.Play7andClub => case(NewState):
                GameState.StandardTurn => block{Rule7Text.HideText(),SkipText.HideText()}
                GameState.Play2orPickup2 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => 
                GameState.Play7andHeart => 
                GameState.Play7andSpade => 
                GameState.Play7andClub => 
                GameState.Play8orSkip => block{Rule7Text.HideText(),SkipText.HideText(),Rule8Text.ShowText()}
                GameState.PlayKingorPickup5 => block{Rule7Text.HideText(),SkipText.HideText(),GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.Play8orSkip => case(NewState):
                GameState.StandardTurn => Rule8Text.HideText()
                GameState.Play2orPickup2 => block{Rule8Text.HideText();GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => block{Rule8Text.HideText();Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andHeart => block{Rule8Text.HideText();Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andSpade => block{Rule8Text.HideText();Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andClub => block{Rule8Text.HideText();Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play8orSkip => 
                GameState.PlayKingorPickup5 => block{Rule8Text.HideText();GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
            GameState.PlayKingorPickup5 => case(NewState):
                GameState.StandardTurn => RemovePickupCount()
                GameState.Play2orPickup2 => block{RemovePickupCount(),GetPickupCountText(2).ShowText(),Rule2Text.ShowText()}
                GameState.Play7andDiamond => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andHeart => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andSpade => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play7andClub => block{RemovePickupCount(),Rule7Text.ShowText(),SkipText.ShowText()}
                GameState.Play8orSkip => block{RemovePickupCount(),Rule8Text.ShowText(),SkipText.ShowText()}
                GameState.PlayKingorPickup5 => block{RemovePickupCount();GetPickupCountText(5).ShowText(),Rule13Text.ShowText()}
        set State = NewState
    HandleGame<override>()<suspends>:void:=
        Visualiser := TurnVitualiser{}
        loop:
            SkipText.HideText()
            Pickup1Text.HideText()
            Pickup2Text.HideText()
            Pickup3Text.HideText()
            Pickup4Text.HideText()
            Pickup5Text.HideText()
            Rule2Text.HideText()
            Rule7Text.HideText()
            Rule8Text.HideText()
            Rule11Text.HideText()
            Rule13Text.HideText()
            if(Player1Text := PlayerText[0],Player2Text := PlayerText[1],Player3Text := PlayerText[2],Player4Text := PlayerText[3]):
                Player1Text.HideText()
                Player2Text.HideText()
                Player3Text.HideText()
                Player4Text.HideText()
            if(Paused = true):
                loop:
                    Sleep(1.0)
                    if(Paused = false):
                        break
            if(P1:=HandAreas[0]):
                set CurrentPlayer = (0,P1)
            set Reversed = false
            DeckArea.QuickShuffle(7)
            DealCards(5)
            Sleep(1.5)
            if(Card := DeckArea.TopCard?):
                PlayArea.DropHoldable(Card)
                set LastPlayedCard = option{Card}
            var EndCount:int := 0
            loop:
                if(Paused = true):
                    loop:
                        Sleep(1.0)
                        if(Paused = false):
                            break
                else:
                    Sleep(1.1)
                if(State = GameState.StandardTurn):
                    AddCardsToPlayer(1)
                if(EndCount = 4):
                    break
                else:
                    if(AI := AIHandZone[CurrentPlayer(1)]):
                        Sleep(1.0)
                        NewCard := AI.ChooseNextCard(LastPlayedCard, State)
                        DeckCount := CurrentPlayer(1).HandArray.Length
                        case(State):
                            GameState.Play2orPickup2 => Play2orPickup2(NewCard)
                            GameState.Play7andDiamond => Play7andSuit(CardType.Diamond,NewCard)
                            GameState.Play7andHeart => Play7andSuit(CardType.Heart,NewCard)
                            GameState.Play7andSpade => Play7andSuit(CardType.Spade,NewCard)
                            GameState.Play7andClub => Play7andSuit(CardType.Club,NewCard)
                            GameState.Play8orSkip =>Play8orSkip(NewCard)
                            GameState.PlayKingorPickup5 => PlayKingorPickup5(NewCard)
                            _ => block{StandardPlay(NewCard),if(not NewCard?, not DeckArea.TopCard? or DeckCount = 8){set EndCount += 1}}
                        if(NewCard?){set LastPlayedCard = NewCard;set EndCount = 0}
                    else if(Player := HandZone[CurrentPlayer(1)], Player.HandArray.Length <> 0 or DeckArea.GetCardCount() <> 0):
                        Sleep(1.0)
                        if(Con := BackRef.Controller.SelectorRef?){set Con.Lock = false}
                        if(State <> GameState.Play2orPickup2 and State <> GameState.PlayKingorPickup5){SkipText.ShowText()}
                        if(AI1:=HandAreas[1],AI2:=HandAreas[2],AI3:=HandAreas[3]):
                            Visualiser.SimulatePlayerTurns(State, Reversed, LastPlayedCard, Player.HandArray, AI1.HandArray, AI2.HandArray, AI3.HandArray)
                        NewCard := race:
                            block{BackRef.UpdateSelected.Signal(Player.SelectedCard);ChooseCard.Await()}
                            BackRef.Visualise(Visualiser)
                        
                        DeckCount := CurrentPlayer(1).HandArray.Length
                        case(State):
                            GameState.Play2orPickup2 => block{Play2orPickup2(NewCard);Sleep(1.0)}
                            GameState.Play7andDiamond => Play7andSuit(CardType.Diamond,NewCard)
                            GameState.Play7andHeart => Play7andSuit(CardType.Heart,NewCard)
                            GameState.Play7andSpade => Play7andSuit(CardType.Spade,NewCard)
                            GameState.Play7andClub => Play7andSuit(CardType.Club,NewCard)
                            GameState.Play8orSkip =>Play8orSkip(NewCard)
                            GameState.PlayKingorPickup5 => block{PlayKingorPickup5(NewCard);Sleep(1.0)}
                            _ => block{StandardPlay(NewCard),if(not NewCard?, not DeckArea.TopCard? or DeckCount = 8){set EndCount += 1}}
                        if(NewCard?){set LastPlayedCard = NewCard;set EndCount = 0}
                        if(Con := BackRef.Controller.SelectorRef?){set Con.Lock = true}
                        SkipText.HideText()
                        Sleep(0.5)
                    else:
                        NextPlayer()
            var EndOut:logic := true
            for(Hand:HandAreas, Hand.HandArray.Length <> 0){set EndOut = false}
            if(EndOut = true):
                LastWinText.ShowText()
                LastLoseText.HideText()
                BackRef.WinMessage.Show()
            else:
                LastLoseText.ShowText()
                LastWinText.HideText()
                BackRef.LoseMessage.Show()
            Sleep(0.5)
            PlayArea.ReturnCards()
            for(Hand:HandAreas){DeckArea.Deck.AddVirtualCards(Hand.ReturnCards())}
            Sleep(0.5)
            var CardsList:[]Virtual_Card := array{}
            for(Suit:array{CardType.Diamond,CardType.Heart,CardType.Spade,CardType.Club}){set CardsList += for(Index:=1..13){Virtual_Card{Suit:=Suit, Value := Index}}}
            DeckArea.SetNewDeck(CardsList)
            Sleep(4.0)

TurnVitualiser := class():
    var SimulatedCards:[Virtual_Card][]Virtual_Card:=map{}

    SimulatePlayerTurns(State:GameState, Reversed:logic, LastCard:?Virtual_Card, PlayerCards:[]Virtual_Card,AI2Cards:[]Virtual_Card,AI3Cards:[]Virtual_Card,AI4Cards:[]Virtual_Card):void:=
        set SimulatedCards = map{}
        for(CardIndex := 0..PlayerCards.Length-1;PlayerCard:=PlayerCards[CardIndex]; PlayerCard.Value <> 7;PlayerCard.Value = 1 or PlayerCard.Value = LastCard?.Value or PlayerCard.Suit = LastCard?.Suit):
            var TempAI2:[]Virtual_Card := AI2Cards
            var TempAI3:[]Virtual_Card := AI3Cards
            var TempAI4:[]Virtual_Card := AI4Cards
            var PlayerIndex:int :=0
            var SetState:GameState := State
            var SetReversed:logic := Reversed
            if(LastCard?.Value <> 7):
                case(PlayerCard.Value):
                    2 => set SetState = GameState.Play2orPickup2
                    7 => case(PlayerCard.Suit):
                        CardType.Diamond => set SetState = GameState.Play7andDiamond
                        CardType.Heart => set SetState = GameState.Play7andHeart
                        CardType.Spade => set SetState = GameState.Play7andSpade
                        CardType.Club => set SetState = GameState.Play7andClub
                    8 => set SetState = GameState.Play8orSkip
                    11 => if(SetReversed=true){ set SetReversed =false}else{ set SetReversed =true}
                    13 => set SetState = GameState.PlayKingorPickup5
                    _ =>
            var PreviousCard:?Virtual_Card := option{PlayerCard}
            var CardArray:[]Virtual_Card := array{}
            loop:
                if(PlayerIndex <> 5):
                    if(SetState <> GameState.Play7andDiamond and SetState <> GameState.Play7andHeart and SetState <> GameState.Play7andSpade and SetState <> GameState.Play7andClub):
                        set PlayerIndex = NextPlayer(SetReversed,PlayerIndex)
                    NextCard:?Virtual_Card :=case(PlayerIndex):
                        1 => SimulateAITurn(TempAI2, PreviousCard, SetState)
                        2 => SimulateAITurn(TempAI3, PreviousCard, SetState)
                        3 => SimulateAITurn(TempAI4, PreviousCard, SetState)
                        _ => false
                    if(Card := NextCard?):
                        case(PlayerIndex):
                            1 => set TempAI2 = for(AICard:TempAI2, Card <> AICard){AICard}
                            2 => set TempAI3 = for(AICard:TempAI3, Card <> AICard){AICard}
                            3 => set TempAI4 = for(AICard:TempAI4, Card <> AICard){AICard}
                            _ => 
                        set CardArray += array{Card}
                        set PreviousCard = NextCard
                        case(Card.Value):
                            2 => set SetState = GameState.Play2orPickup2
                            7 => case(PlayerCard.Suit):
                                CardType.Diamond => set SetState = GameState.Play7andDiamond
                                CardType.Heart => set SetState = GameState.Play7andHeart
                                CardType.Spade => set SetState = GameState.Play7andSpade
                                CardType.Club => set SetState = GameState.Play7andClub
                            8 => set SetState = GameState.Play8orSkip
                            11 => set SetReversed = if(SetReversed=true){false}else{true}
                            13 => set SetState = GameState.PlayKingorPickup5
                            _ =>
                    else:
                        set SetState = GameState.StandardTurn
                else{break}
            if(set SimulatedCards[PlayerCard]=CardArray){}

    SimulateAITurn(CardArray:[]Virtual_Card, isTestCard:?Virtual_Card, Play:GameState):?Virtual_Card:=
        if(TestCard:=isTestCard?):
            case(Play):
                GameState.Play2orPickup2 => block{for(Card:CardArray, Card.Value = 2){return option{Card}};return false}
                GameState.Play7andDiamond => block{for(Card:CardArray, Card.Suit = CardType.Diamond){return option{Card}};return false}
                GameState.Play7andHeart => block{for(Card:CardArray, Card.Suit = CardType.Heart){return option{Card}};return false}
                GameState.Play7andSpade => block{for(Card:CardArray, Card.Suit = CardType.Spade){return option{Card}};return false}
                GameState.Play7andClub => block{for(Card:CardArray, Card.Suit = CardType.Club){return option{Card}};return false}
                GameState.Play8orSkip => block{for(Card:CardArray, Card.Value = 8){return option{Card}};return false}
                GameState.PlayKingorPickup5 => block{for(Card:CardArray, Card.Value = 13){return option{Card}};return false}
                _ => return PickStandard(CardArray,TestCard)
        else:
            return false
    PickStandard(CardArray:[]Virtual_Card, TestCard:Virtual_Card):?Virtual_Card:=
        for(Card:CardArray, TestCard.Suit = Card.Suit and TestCard.Value = 7){return option{Card}}
        for(Card:CardArray, TestCard.Suit = Card.Suit or (TestCard.Value = Card.Value <> 1)){return option{Card}}
        var Aces:[]Virtual_Card := array{}
        var SuitCounts:[CardType]int := map{CardType.Diamond => 0,CardType.Heart => 0,CardType.Spade => 0,CardType.Club => 0}
        for(Card:CardArray){if(Card.Value = 1){set Aces+=array{Card}}else{if(set SuitCounts[Card.Suit]+=1){}}}
        if(var LargestCard:Virtual_Card := Aces[0]):
            if(Aces.Length>1):
                for(Index:=1..Aces.Length, TestAce := Aces[Index], SuitCounts[TestAce.Suit]>SuitCounts[LargestCard.Suit]){set LargestCard = TestAce}
            return option{LargestCard}
        return false

    NextPlayer(Reversed:logic, CurrentIndex:int):int:=
        if(Reversed = false):
            if(CurrentIndex<3):
                return CurrentIndex+1
            else:
                return 5
        else:
            if(CurrentIndex>0):
                if(CurrentIndex>1):
                    return CurrentIndex-1
                else:
                    return 5
            else:
                return 3

I just got done playing like 10 rounds. Fun game and definitely outside the realm of shooter. I was wondering how/where you learned to not have a 3rd person layout like that. can you point me to any resources that you used? or give a run down of the steps you took.

thanks in advance.

Thanks! The project didn’t start with a clear design or established workflow. I began by implementing a custom mouse system, which led me to create a simple card framework for the mouse to interact with. To support more complex card interactions, I introduced a switch mechanic. To help with testing, I built an AI player, and during that process I realized the AI-driven gameplay could put a spin on the standard switch gameplay. At that point, I decided to add controller support and simulated the AI player’s behavior to enable AI move prediction to complement the gameplay.

The code is pretty terrible and I wouldn’t recommend copying much of it as there is a lot of issues with it. This was a one week project so the code quickly became just about getting it to work but I’ll drop the full verse code in the initial post, hopefully it can be a somewhat useful resource.