Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Solitare
- A card game by Nitrogen Fingers
- ]]--
- --[[ Cards.lua API
- Currently in the same source file but might separate out
- Everything below are the features usable by this engine to make a
- simple card game. It creates cards and displays them, as well as
- helpers to make interaction with other cards easier.
- You implement the mechanics, and you have a card game on your hands!
- ]]--
- --The layout on each card (except aces, which vary from suit to suit)
- --Numbered values are the front, named styles are backs
- --Values: 2-10 as read, 1 == ace, 11 = jack, 12 = queen, 13 = king
- local styles = {
- [0] = { "| "; "| "; "| "; "|___" };
- [2] = { "2 ;"; " ; "; " "; " ; " };
- [3] = { "3 ;"; " ; ;"; " "; " ; " };
- [4] = { "4 ;"; " ; ;"; " "; " ; ;" };
- [5] = { "5 ;"; " ; ;"; " ; "; " ; ;" };
- [6] = { "6 ;"; " ; ;"; " ; ;"; " ; ;" };
- [7] = { "7 ;"; " ; ;"; " ;;;"; " ; ;" };
- [8] = { "8 ;"; " ; ;"; " ;;;"; " ;;;" };
- [9] = { "9 ;"; " ;;;"; " ;;;"; " ;;;" };
- [10] = { "10 ;"; " ;;;"; ";;;;"; " ;;;" };
- [11] = { "J ;"; " (>|"; " ** "; "|<) " };
- [12] = { "Q ;"; " (>|"; " -- "; "|<) " };
- [13] = { "K ;"; " (>|"; " ## "; "|<) " };
- --Styles of the back of the card
- backs = {
- alligator = { ">##<"; ">##<"; ">##<"; ">##<" };
- rounded = { "/ \\"; " || "; " || "; "\\ /" };
- royal = { " oo "; "-/\\-"; "-\\/-"; " oo " };
- frame = { "/--\\"; "| |"; "| |"; "\\--/" };
- spooky = { "\\ /"; "-()-"; "-()-"; "/ \\" };
- };
- --The style regex. Replace this character with the suit.char
- --Don't use this as part of card styling (change if necessary)
- styleregex = ";";
- --Width and height of the cards
- width = 4; height = 4;
- }
- --The style the back of the card uses
- local backStyle = styles.backs.rounded
- --The colours the card back uses
- local backbg = colours.blue
- local backfg = colours.white
- --The colour of the table
- local tablebg = colours.green
- --The colour of the face of the cards (should probably be white)
- local facecol = colours.white
- --The colour the face changes when a card is selected
- local selcol = colours.yellow
- --The colour of the outline of a 'space' where cards can be placed
- local emptycol = colours.grey
- --The X and Y offset of the screen. You can change this to allow
- --more cards than would otherwise appear (use wisely)
- local xoff, yoff = 0,0
- --The suits a card can have and how they look
- local suits = {
- spades = {
- colour = colours.black;
- char = "^";
- ace = { "A ".."^"; " /\\ "; "(__)"; " /\\ " }
- };
- diamonds = {
- colour = colours.red;
- char = "*";
- ace = { "A *"; " /\\ "; "< >"; " \\/ " }
- };
- clubs = {
- colour = colours.black;
- char = "+";
- ace = { "A +"; " () "; "()()"; " /\\ " }
- };
- hearts = {
- colour = colours.red;
- char = "v";
- ace = { "A v"; "/\\/\\"; "\\ /"; " \\/ " }
- };
- }
- --Changes the numerical code of the card value to a printable string
- local function valToDisplay(_val)
- if _val == 1 then return "A "
- elseif _val == 10 then return "10"
- elseif _val == 11 then return "J "
- elseif _val == 12 then return "Q "
- elseif _val == 13 then return "K "
- else return _val.." " end
- end
- --A singly linked list prototype for containing collections of cards
- --Card games often layer cards on one another so this is a good way to
- --keep track of them and make moving lots of cards around easier.
- local linkedCardP = {
- --Head of the list
- head = nil,
- --Tail of the list
- tail = nil,
- display = function(self)
- if self.head then self.head:display() end
- end;
- reverse = function(self)
- if not self.head and self.head.tail then return end
- local _head = self.head
- local function recRev(_head)
- local _rest = _head.tail
- if _rest then
- recRev(_rest)
- _rest.tail = _head
- else
- self.head = _head
- end
- end
- recRev(_head)
- _head.tail = nil
- end;
- }
- --Constructs a
- function linkedCardP:new(o)
- o = o or { }
- setmetatable(o, self)
- self.__index = self
- return o
- end;
- --The card prototype
- local cardP = {
- --The value on the face of the card (2-A)
- val = 0,
- --The suit of the card, from the suits table
- suit = suits.spades,
- --Whether or not the card is turned face-up
- visible = false,
- --Whether or not the card is selected
- selected = false,
- --The card directly on top of this card, if any
- tail = nil,
- --Whether or not the card runs a display call at all. This is used
- --when the cards would otherwise clutter the display area
- hidden = false,
- --Used for the coordinates of the card, as X-Y
- x = 0, y = 0,
- --Displays an outline of the card painted as the table
- --A method to decrease the number of necessary draw calls
- displayTable = function(self)
- for _y = self.y, self.y + styles.height do
- term.setBackgroundColour(tablebg)
- term.setCursorPos(self.x + xoff, _y + yoff)
- term.write(string.rep(" ", styles.width))
- end
- end;
- --Displays the face or back of a card, and its children if they
- --are not being overlapped
- display = function(self)
- if self.tail and not (self.tail.x == self.x
- and self.tail.y == self.y) then
- self.tail:display()
- end
- if self.hidden then return end
- local _style = nil
- if self.val == 0 then
- _style = styles[self.val]
- term.setBackgroundColour(tablebg)
- term.setTextColour(emptycol)
- elseif not self.visible then
- _style = backStyle
- term.setBackgroundColour(backbg)
- term.setTextColour(backfg)
- else
- if self.selected then
- term.setBackgroundColour(selcol)
- else
- term.setBackgroundColour(facecol)
- end
- term.setTextColour(self.suit.colour)
- if self.val == 1 then _style = self.suit.ace
- else _style = styles[self.val] end
- end
- for i = 1, #_style do
- term.setCursorPos(self.x + xoff, self.y + i - 1 + yoff)
- term.write(string.gsub(_style[i], ";", self.suit.char))
- end
- end;
- --Move this card (and all cards on top of it) to a new position
- --ChildMove will move all cards in the tail
- move = function(self, newx, newy, childMove)
- local _xdiff,_ydiff = newx - self.x, newy - self.y
- if childMove and self.tail then self.tail:move(
- self.tail.x + _xdiff, self.tail.y + _ydiff, true) end
- self.x = newx
- self.y = newy
- end;
- --Determine whether or not an xy point lies on the body of the card
- clicked = function(self, mousex, mousey)
- return mousex >= self.x + xoff and mousex < self.x + styles.width + xoff and
- mousey >= self.y + yoff and mousey < self.y + styles.height + yoff and not self.hidden
- end;
- --Determines whether or not two cards are overlapping
- overlapping = function(self, ocard)
- return self.x >= ocard.x - styles.width and
- self.x <= ocard.x + styles.width and
- self.y >= ocard.y - styles.height and
- self.y <= ocard.y + styles.height and not ocard.hidden and not self.hidden
- end;
- __tostring = function(self)
- return self.val.." of "..self.suit.char
- end
- }
- --Constructs a new card
- function cardP:new (o)
- o = o or {}
- setmetatable(o, self)
- self.__index = self
- return o
- end;
- --The main deck, from which you should draw all cards
- local deck = {}
- --Creates a fresh deck of 52 cards in suit and number order
- --Count is the number of decks you wish to include
- local function createDeck(_count)
- deck = {}
- for i = 1, _count do
- for _,suitval in pairs(suits) do
- for _val = 1,13 do
- table.insert(deck, cardP:new({
- val = _val;
- suit = suitval;
- }))
- end
- end
- end
- end
- --Shuffles the deck. A close-to-optimal shuffle
- local function shuffleDeck()
- for i=#deck,2,-1 do
- local _card = table.remove(deck, math.random(1, i))
- table.insert(deck, i, _card)
- end
- end
- --[[ Overmenu Facilities
- The glue that ties each of the games together
- ]]--
- --Screen Size
- local w,h = term.getSize()
- --The function to call to draw the entire game again as needed
- local currOverDraw = nil
- --Whether or not the user has just quit
- local quit = false
- --The next game to be played
- local nextGame = nil
- --The options displayed in the menu
- local menuOptions = { name = "Games", x = 3, y = 1 }
- --The title at the top of the screen
- local title = "Card Games"
- --Draws the menu bar. Should be the last thing in every overdraw call
- local function displayOverMenuBar()
- term.setCursorPos(3,1)
- term.setBackgroundColour(colours.lightGrey)
- term.clearLine()
- term.setTextColour(colours.black)
- term.write("Games")
- term.setTextColour(colours.grey)
- term.setCursorPos(w/2 - #title/2, 1)
- term.write(title)
- end
- --[[Produces a nice dropdown menu based on a table of strings. Depending on the position, this will auto-adjust the position
- of the menu drawn, and allows nesting of menus and sub menus. Clicking anywhere outside the menu will cancel and return nothing
- Params: x:int = the x position the menu should be displayed at
- y:int = the y position the menu should be displayed at
- options:table = the list of options available to the user, as strings or submenus (tables of strings, with a name parameter)
- Returns:string the selected menu option.
- ]]--
- local function displayDropDown(x, y, options, noTitle)
- inDropDown = true
- if noTitle then y = y - 1 end
- --Figures out the dimensions of our thing
- local longestX = #options.name
- for i=1,#options do
- local currVal = options[i]
- if type(currVal) == "table" then currVal = currVal.name end
- longestX = math.max(longestX, #currVal)
- end
- local xOffset = math.max(0, longestX - ((w-2) - x) + 1)
- local yOffset = math.max(0, #options - ((h-1) - y))
- local clickTimes = 0
- local tid = nil
- local selection = nil
- while clickTimes < 4 do
- if not noTitle then
- term.setCursorPos(x-xOffset,y-yOffset)
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(options.name.." ")
- end
- for i=1,#options do
- term.setCursorPos(x-xOffset, y-yOffset+i)
- local currVal = options[i]
- if type(currVal.value) == "table" then
- currVal.enabled = #currVal.value > 0
- end
- if i==selection and clickTimes % 2 == 0 then
- term.setBackgroundColour(colours.blue)
- if options[selection].enabled then term.setTextColour(colours.white)
- else term.setTextColour(colours.grey) end
- else
- term.setBackgroundColour(colours.lightGrey)
- local _tcol = colours.black
- if not currVal.enabled then _tcol = colours.grey end
- term.setTextColour(_tcol)
- end
- if type(currVal.value) == "table" then
- term.write(currVal.name..string.rep(" ", longestX-#currVal.name))
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(">")
- else
- term.write(currVal.name..string.rep(" ", longestX-#currVal.name + 1))
- end
- if (i~= selection or clickTimes %2 == 1) and currVal.key and currVal.enabled then
- term.setTextColour(colours.blue)
- term.setBackgroundColour(colours.lightGrey)
- local co = currVal.name:find(currVal.key)
- if not co then co = longestX else co = co - 1 end
- term.setCursorPos(x-xOffset+co, y-yOffset + i)
- term.write(currVal.key)
- end
- end
- local id, p1, p2, p3 = os.pullEvent()
- if id == "timer" then
- if p1 == tid then
- clickTimes = clickTimes + 1
- if clickTimes > 2 then
- break
- else
- tid = os.startTimer(0.1)
- end
- elseif p1 == bTimer then saveDocument(bPath) end
- elseif id == "key" and not tid then
- if p1 == keys.leftCtrl then
- selection = ""
- break
- elseif p1 == keys.down and (not selection or selection < #options) then
- selection = selection or 0
- _os = selection
- repeat selection = selection + 1
- until selection == #options + 1 or options[selection].enabled
- --if selection == #options + 1 then selection = _os end
- elseif p1 == keys.up and (not selection or selection > 1) then
- selection = selection or #options + 1
- _os = selection
- repeat selection = selection - 1
- until selection == 0 or options[selection].enabled
- if selection == 0 then selection = _os end
- elseif p1 == keys.enter and selection and options[selection].enabled then
- tid = os.startTimer(0.1)
- clickTimes = clickTimes - 1
- end
- elseif id == "mouse_click" and not tid then
- local _xp, _yp = x - xOffset, y - yOffset
- if p2 >= _xp and p2 <= _xp + longestX + 1 and
- p3 >= _yp+1 and p3 <= _yp+#options then
- if options[p3 - _yp].enabled then
- selection = p3-(_yp)
- tid = os.startTimer(0.1)
- end
- else
- selection = ""
- break
- end
- elseif id == "char" and not tid then
- for k,v in ipairs(options) do
- if v.key and v.key:lower() == p1:lower() and options[k].enabled then
- selection = k
- tid = os.startTimer(0.1)
- break
- end
- end
- end
- end
- local _val = selection
- if type(selection) == "number" then
- selection = options[selection].value
- end
- if type(selection) == "string" then
- inDropDown = false
- return selection
- elseif type(selection) == "table" then
- return displayDropDown(x + longestX + 1, y + _val, selection, true)
- elseif type(selection) == "function" then
- local _rval = selection()
- if not _rval then return "" else return _rval end
- end
- end
- --Handles mouse events for the menu, returns true or false if something happens
- local function manageMenuMouseEvents(_id, _x, _y)
- if _id == "mouse_click" and _y == menuOptions.y and _x >= menuOptions.x and
- _x < menuOptions.x + #menuOptions.name then
- local _rval = displayDropDown(menuOptions.x, menuOptions.y, menuOptions)
- if currOverDraw then currOverDraw()
- else
- term.setBackgroundColour(tablebg)
- term.clear()
- displayOverMenuBar()
- end
- end
- return nextGame ~= nil or quit
- end
- --[[ Game Variant: Klondike
- The classic game of solitare. Seven columns of cards, try to
- sort your deck by suit and number.
- ]]--
- --The selected card, and the pile it belongs to
- local selCard, selDeck, selSpace = nil, nil, nil
- --The space, waste and tableau decks
- local spaces, waste, tableaus = {}, linkedCardP:new(), {}
- --The deck's default position
- local deckX,deckY = 0,0
- --An empty card representing where the deck should be
- local deckSpace = nil
- --The last position of a mouse click not intercepting a card
- local lmX,lmY = 0,0
- --And a temporary offset value so we don't keep reapplying
- local txoff, tyoff = 0,0
- --The player's score at Klondike
- local klondikeScore = 0
- --Shows the score at the bottom of the screen
- local function displayScore()
- term.setCursorPos(1,h)
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.black)
- term.write(" Score: "..klondikeScore)
- end
- --Draws every element in Klondike. There's not much redraw in this game
- --so I haven't bothered to optimize too heavily.
- local function drawKlondike()
- term.setBackgroundColour(tablebg)
- term.clear()
- for i=1,7 do
- if spaces[i] then spaces[i]:display() end
- end
- waste:display()
- for i=1,4 do
- if tableaus[i] then tableaus[i]:display() end
- end
- if deckSpace then deckSpace:display() end
- displayScore()
- displayOverMenuBar()
- end
- --Sets up a new game of Klondike, organizing the deck.
- local function makeKlondikeSetup()
- createDeck(1)
- shuffleDeck()
- spaces = {}
- --Cleanup from last game
- if deckSpace then deckSpace.val = 1 end
- if waste then waste.head = nil end
- for i=1,4 do
- if tableaus[i] then tableaus[i].head = cardP:new({
- x = tableaus[i].head.x;
- y = tableaus[i].head.y;
- val = 0;
- })
- end
- end
- for i=1,7 do spaces[i] = nil end
- --The main play area
- for i=1,7 do
- for j=i,7 do
- --Our (silly) little card animation :D
- drawKlondike()
- sleep(0.1)
- if i == 1 then spaces[j] = linkedCardP:new() end
- local _card = table.remove(deck, 1)
- _card.x = 1 + 1 * j + styles.width * (j - 1)
- _card.y = i + 2
- if spaces[j].head then _card.tail = spaces[j].head
- else spaces[j].tail = _card end
- spaces[j].head = _card
- if i == j then spaces[j].head.visible = true end
- end
- end
- --The waste pile and remaining deck
- deckX = spaces[#spaces].head.x + 2 + styles.width
- deckY = 2
- for i=1,#deck do
- deck[i].x = deckX
- deck[i].y = deckY
- end
- deckSpace = cardP:new({ x = deckX; y = deckY; val = 1})
- --The tableau piles
- for i=1,4 do
- local _ecard = cardP:new()
- _ecard.val = 0
- if i % 2 == 0 then _ecard.x = deckX
- else _ecard.x = deckX + styles.width + 2 end
- if i <= 2 then _ecard.y = deckY + styles.height * 2
- else _ecard.y = deckY + styles.height * 3 + 1 end
- tableaus[i] = linkedCardP:new()
- tableaus[i].head = _ecard
- end
- end
- --Determines whether or not the game is won
- local function checkWinState()
- local _won = true
- for i=1,#tableaus do
- _won = tableaus[i].head and tableaus[i].head.val == 13
- if not _won then break end
- end
- return _won
- end
- --Selects a given card
- local function select(_card, _deck, _space)
- if selCard then selCard.selected = false end
- selCard = _card
- selDeck = _deck
- selSpace = _space
- if selCard then selCard.selected = true end
- end
- --Determines how many points were scored (or lost) for making a specific move. Uses Windows Solitare conventions
- local function evaluateScore(initDeck, finalDeck)
- if finalDeck == tableaus then
- klondikeScore = klondikeScore + 10
- elseif initDeck == waste then
- klondikeScore = klondikeScore + 5
- elseif initDeck == tableaus then
- klondikeScore = klondikeScore - 15
- end
- displayScore()
- end
- --Adds a card to the selected tableau. Returns true if the check was successful
- local function addKlondikeTableau(_card, _deck)
- if selCard == selDeck.head and selCard.val == _card.val + 1 and
- (_card.val == 0 or _card.suit == selCard.suit) then
- selDeck.head = selCard.tail
- if selSpace == spaces and selDeck.head == nil then
- selDeck.head = cardP:new( { x = selCard.x; y = selCard.y; val = 0; } )
- end
- selCard.x = _deck.head.x
- selCard.y = _deck.head.y
- if _deck.head.val == 0 then _deck.head = nil end
- selCard.tail = _deck.head
- _deck.head = selCard
- evaluateScore(_deck, tableaus)
- else
- return false
- end
- select()
- return true
- end
- --Moves one card and it's peers to be on top of another card in the play area. Implements
- --rules associated with this kind of move
- local function shiftKlondikeCard(_card, _deck)
- --Ensuring the move is legal. If it isn't we just select the new card
- local isLegal = (_card.suit.colour ~= selCard.suit.colour and _card.val == selCard.val + 1 and
- _card == _deck.head) or (_card.val == 0 and selCard.val == 13)
- if not isLegal then
- local _cDeck = selDeck
- select(_card, _deck, spaces)
- _cDeck:display()
- _deck:display()
- return
- end
- --Moving a card from the waste pile into the play area
- if selSpace == waste then
- selCard:displayTable()
- selCard:move(_card.x, _card.y + 1)
- _deck.head = selCard
- selDeck.head = selCard.tail
- selDeck:display()
- --Moving a card from one part of the play field to the other
- elseif selSpace == spaces then
- local _top = selDeck.head
- _deck.head = _top
- local _ydiff = 1
- _top:displayTable()
- while _top ~= selCard do
- _top = _top.tail
- _ydiff = _ydiff + 1
- _top:displayTable()
- end
- selDeck.head = selCard.tail
- if selDeck.head == nil then
- selDeck.head = cardP:new({ val = 0, x = selCard.x, y = selCard.y })
- end
- selDeck:display()
- selCard.tail = nil
- _deck.head:move(_card.x, _card.y + _ydiff, true)
- --Moving a card from the tableau onto the play area
- elseif selSpace == tableaus then
- selDeck.head = selCard.tail
- if selDeck.head == nil then
- selDeck.head = cardP:new({ val = 0, x = selCard.x, y = selCard.y })
- end
- selDeck:display()
- selCard:move(_card.x, _card.y + 1)
- _deck.head = selCard
- end
- selCard.tail = _card
- evaluateScore(selSpace, spaces)
- if _card.val == 0 then
- selCard.tail = nil
- _deck.head:move(_deck.head.x, _deck.head.y - 1, true)
- end
- select()
- _deck:display()
- end
- --Completes the game for us, when all cards are revealed and in the play area
- local function autoCompleteKlondike()
- local _lowestVal = nil
- repeat
- _lowestVal = math.huge
- for i = 1, #spaces do
- if spaces[i].head.val > 0 then
- _lowestVal = math.min(_lowestVal, spaces[i].head.val)
- end
- end
- for i = 1, #spaces do
- if spaces[i].head.val == _lowestVal then
- select(spaces[i].head, spaces[i], spaces)
- for j = 1, #tableaus do
- if addKlondikeTableau(tableaus[j].head, tableaus[j]) then break end
- end
- end
- end
- drawKlondike()
- sleep(0.2)
- until _lowestVal >= 13
- end
- --Handles all input for Klondike
- local function handleKlondikeInput()
- gameOver, gameWon = false, false
- while not gameOver do
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then return end
- if id == "mouse_click" then
- local complete = false
- lmX, lmY = 0,0
- for i=1,#spaces do
- local _c = spaces[i].head
- while _c do
- --We've clicked a card in the main hand
- if _c:clicked(p2,p3) then
- --Performing a doubleclick
- if selCard == _c then
- local _cDeck = selDeck
- select()
- _cDeck:display()
- complete = true
- break
- --Revealing a newly freed card
- elseif _c == spaces[i].head and _c.visible == false and _c.val > 0 then
- _c.visible = true
- local _cDeck = selDeck
- select()
- if _cDeck then _cDeck:display() end
- klondikeScore = klondikeScore + 5
- displayScore()
- _c:display()
- complete = true
- break
- --Selecing a card with no other selection
- elseif selCard == nil and _c.visible == true and _c.val > 0 then
- select(_c, spaces[i], spaces)
- spaces[i]:display()
- complete = true
- break
- --Attempting to move a previously selected card onto this one
- elseif selCard then
- shiftKlondikeCard(_c, spaces[i])
- drawKlondike()
- complete = true
- break
- end
- end
- _c = _c.tail
- end
- if complete then break end
- end
- for i=1,#tableaus do
- local _c = tableaus[i].head
- if _c:clicked(p2,p3) then
- --Attempting to add a card to the tableau
- if selCard and addKlondikeTableau(_c, tableaus[i]) then
- gameWon = checkWinState()
- gameOver = gameWon
- drawKlondike()
- --Selecting a card already on the tableau
- elseif _c.val > 0 then
- local _cDeck = selDeck
- select(_c, tableaus[i], tableaus)
- tableaus[i]:display()
- if _cDeck then _cDeck:display() end
- end
- complete = true
- break
- end
- end
- if deckSpace:clicked(p2,p3) then
- if waste.head and waste.head.selected then select() end
- --Refreshing the deck
- if deckSpace.val == 0 then
- while waste.head do
- if not waste.head.hidden then waste.head:displayTable() end
- table.insert(deck, 1, waste.head)
- waste.head = deck[1].tail
- deck[1].visible = false
- deck[1].hidden = false
- deck[1].tail = nil
- end
- deckSpace.val = #deck
- deckSpace:display()
- --Dealing another 3 cards to the waste deck
- else
- local _h = waste.head
- while _h and not _h.hidden do
- _h.hidden = true
- _h:displayTable()
- _h = _h.tail
- end
- for i = 1, math.min(#deck, 3) do
- local _c = table.remove(deck,1)
- _c.tail = waste.head
- _c:move(deckSpace.x + styles.width + 2, deckSpace.y + i - 1)
- _c.visible = true
- waste.head = _c
- deckSpace.val = #deck
- --More animations!
- waste:display()
- displayOverMenuBar()
- sleep(0.1)
- end
- deckSpace:display()
- end
- end
- if waste.head and waste.head:clicked(p2,p3) then
- --Selecting a card in the waste deck
- if waste.head.selected then
- select()
- else
- local _d = selDeck
- select(waste.head, waste, waste)
- if _d then _d:display() end
- end
- waste.head:display()
- complete = true
- end
- --Deselecting a card by clicking in empty space
- if selCard ~= nil and not complete then
- local _cDeck = selDeck
- select()
- if _cDeck then _cDeck:display() end
- end
- lmX, lmY = p2, p3
- txoff, tyoff = xoff, yoff
- displayOverMenuBar()
- --Determining if the player can autocomplete the game
- if complete and deckSpace.val == 0 and waste.head == nil and not checkWinState() then
- local _autoComplete = true
- for i=#spaces,1,-1 do
- local _h = spaces[i].head
- while _h do
- if _h.val > 0 then
- _autoComplete = _h.visible
- if not _autoComplete then break end
- end
- _h = _h.tail
- end
- if not _autoComplete then break end
- end
- if _autoComplete then
- autoCompleteKlondike()
- gameWon = true
- gameOver = true
- end
- end
- elseif id == "mouse_drag" then
- if lmX > 0 and lmY > 0 then
- local yDiff = p3 - lmY
- --Mouse drag events seem to be pretty overzealous. Or is that a version issue...
- if tyoff + yDiff ~= yoff then
- yoff = math.min(tyoff + yDiff, 0)
- --h-21 is the maximum unrevealed column (6) plus the max revealed column (13)
- --plus 3 to reveal the face of the card and 4 more for whitespace
- yoff = math.max(yoff, h - 26)
- drawKlondike()
- end
- end
- elseif id == "mouse_scroll" then
- yoff = math.min(yoff - p1, 0)
- yoff = math.max(yoff, h - 26)
- drawKlondike()
- elseif id == "key" then
- if p1 == keys.down then
- yoff = math.min(yoff + 1, 0)
- drawKlondike()
- elseif p1 == keys.up then
- yoff = math.max(yoff - 1, h - 26)
- drawKlondike()
- end
- end
- end
- if gameWon then
- --One last silly win animation
- local _spread = {}
- for i=1,#tableaus do
- tableaus[i]:reverse()
- tableaus[i].head:move(w/4 + styles.width/2 + 13, 2 + styles.height * (i - 1), true)
- local _c = tableaus[i].head.tail
- _spread[i] = _c
- while _c do
- _c:move(_c.x - 2 + xoff, _c.y + yoff, true)
- _c.hidden = true
- _c = _c.tail
- end
- end
- while _spread[1] do
- drawKlondike()
- sleep(0.1)
- for i=1,#_spread do
- _spread[i].hidden = false
- _spread[i] = _spread[i].tail
- end
- end
- drawKlondike()
- end
- end
- --Runs a game of Klondike
- local function playKlondike()
- backStyle = styles.backs.rounded
- backbg = colours.blue
- backfg = colours.white
- title = "Klondike Solitaire"
- xoff = 0
- yoff = 0
- makeKlondikeSetup()
- drawKlondike()
- handleKlondikeInput()
- end
- --[[ Game Variant: Blackjack
- Play against a dealer, bet on your dealout and try to have a
- greater score than the dealer without going over 21
- ]]--
- --The player's funds
- local bank = 200
- --The table minimum and maximum
- local tableMin, tableMax = 20, math.huge
- --The current bet, the split bet and any insurance the player has bought
- local currBet,splitBet,insurance = 0,0,0
- --The player's hand
- local hand = linkedCardP:new()
- local splitHand = nil
- --The dealer's hand
- local dealerHand = linkedCardP:new()
- --The position of the betting squares
- local _bx1, _bx2, _by = 14, w - 21, 10
- --The empty 'deck' card, symbolizing the deck to click for a deal
- local _bjDeck = cardP:new( { val = 1; visible = false; x = w - 8; y = 3; } )
- --The footer message and colour
- local _footermsg, _footercolor = "",colours.white
- --Prepares a new game of blackjack
- local function startBlackjack()
- createDeck(1)
- bank = 200
- hand = linkedCardP:new()
- dealerHand = linkedCardP:new()
- currBet = 0
- insurance = 0
- splitBet = 0
- _footermsg, _footercolor = "",colours.white
- end
- --Displays the bets and bank at the bottom of the screen
- local function displayBlackjackBets()
- term.setCursorPos(1,h-1)
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.black)
- term.clearLine()
- term.write(" Bank: $"..bank)
- term.setCursorPos(20, h-1)
- term.setTextColour(_footercolor)
- term.write(_footermsg)
- term.setCursorPos(_bx1 + 1, _by + 1)
- term.write(string.rep(" ", 5))
- if currBet > 0 then
- term.setCursorPos(_bx1 + 1, _by + 1)
- term.write("$"..currBet.." ")
- end
- term.setCursorPos(_bx2 + 1, _by + 1)
- term.write(string.rep(" ", 5))
- if splitBet > 0 then
- term.setCursorPos(_bx2 + 1, _by + 1)
- term.write("$"..splitBet.." ")
- end
- end
- --Displays all elements in a game of blackjack
- local function displayBlackjack()
- term.setBackgroundColour(tablebg)
- term.clear()
- --The table
- term.setTextColour(colours.orange)
- local _tableMsg = "Blackjack pays 3 to 2"
- term.setCursorPos(w/2-#_tableMsg/2, 8)
- term.write(_tableMsg)
- term.setTextColour(colours.white)
- _tableMsg = "Insurance pays 2 to 1"
- term.setCursorPos(w/2-#_tableMsg/2, 9)
- term.write(_tableMsg)
- term.setTextColour(colours.white)
- for i=1,3,2 do
- term.setCursorPos(_bx1 + 1, _by - 1 + i)
- term.write(string.rep("-", 5))
- term.setCursorPos(_bx2 + 1, _by - 1+ i)
- term.write(string.rep("-", 5))
- end
- term.setCursorPos(_bx1, _by + 1)
- term.write("|"..string.rep(" ",5).."|")
- term.setCursorPos(_bx2, _by + 1)
- term.write("|"..string.rep(" ",5).."|")
- --Hands
- hand:display()
- if splitHand then splitHand:display() end
- dealerHand:display()
- displayBlackjackBets()
- _bjDeck:display()
- displayOverMenuBar()
- end
- --Prompts the user to put in how much money they will bet before the next draw
- local function placeBlackjackBet()
- _footermsg = ""
- displayBlackjack()
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.black)
- local _x = 20
- local _prompt = "Place bet: "
- while currBet == nil or currBet < tableMin or currBet > tableMax or currBet > bank do
- term.setCursorPos(_x,h-1)
- term.write(_prompt)
- term.setTextColour(colours.black)
- currBet = read()
- term.setCursorPos(_x + #_prompt, h-1)
- term.write(string.rep(" ", #currBet))
- currBet = tonumber(currBet)
- term.setTextColour(colours.red)
- end
- bank = bank - currBet
- displayBlackjackBets()
- end
- --Deal a hand of blackjack
- local function dealBlackjack()
- createDeck(1)
- local _c = hand.head
- while _c do
- _c:displayTable()
- _c = _c.tail
- end
- local _c = dealerHand.head
- while _c do
- _c:displayTable()
- _c = _c.tail
- end
- hand.head = nil
- dealerHand.head = nil
- splitHand = nil
- shuffleDeck()
- displayBlackjackBets()
- placeBlackjackBet()
- --Deal the cards
- for i=1,4 do
- local _c = table.remove(deck,1)
- if i % 2 == 1 then
- if hand.head then _c.x = hand.head.x + styles.width + 1
- else _c.x = _bx1 - 4 end
- _c.y = h - styles.height - 2
- _c.tail = hand.head
- _c.visible = true
- hand.head = _c
- else
- if dealerHand.head then _c.x = dealerHand.head.x + styles.width + 1
- else _c.x = _bx1 + 4 end
- _c.y = 3
- _c.tail = dealerHand.head
- _c.visible = dealerHand.head ~= nil
- dealerHand.head = _c
- end
- _c:display()
- sleep(0.2)
- end
- end
- --Returns a point value for how much the hand is worth
- local function calculateHandValue(_hand)
- local _score, _c = 0, _hand.head
- local _firstAce = false
- while _c do
- if _c.val == 1 and not _firstAce then
- _score = _score + 11
- _firstAce = true
- elseif _c.val > 10 then
- _score = _score + 10
- else
- _score = _score + _c.val
- end
- _c = _c.tail
- end
- if _score > 21 and _firstAce then
- _firstAce = false
- _score = _score - 10
- end
- return _score, _firstAce
- end
- local function drawHandValue(_x,_y,_value,_soft)
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.setCursorPos(_x,_y)
- term.write(" ".._value..string.rep(" ", 2 - math.floor(math.log10(_value))))
- term.setCursorPos(_x,_y+1)
- if _soft then term.write("soft") else term.write("hard") end
- end
- --Handles events for insurace
- local function handleBlackjackInsurance()
- if dealerHand.head.val == 1 then
- local _insuranceCost = math.floor(currBet / 2)
- _footermsg, _footercolor = "Buy insurance? Y/N", colours.white
- displayBlackjackBets()
- local id,_c,p2,p3 = os.pullEvent()
- while string.lower(_c) ~= 'y' and string.lower(_c) ~= 'n' do
- if manageMenuMouseEvents(id,p2,p3) then return end
- id,_c,p2,p3 = os.pullEvent("char")
- end
- if string.lower(_c) == 'y' then
- insurance = currBet / 2
- end
- end
- end
- --Displays a little message in the betting box
- local function displayBetMessage(_msg, _col)
- term.setCursorPos(_bx1 + 1, _by + 1)
- term.setBackgroundColour(tablebg)
- term.setTextColour(_col)
- term.write(_msg)
- end
- --Handles the input for the basic game of blackjack
- local function handleBlackjackInput()
- handleBlackjackInsurance()
- local _takings = 0
- if calculateHandValue(dealerHand) == 21 then
- sleep(0.2)
- _c = dealerHand.head
- while _c do
- _c.visible = true
- _c = _c.tail
- end
- dealerHand:display()
- _takings = _takings - currBet + (insurance * 2)
- bank = bank + math.max(_takings, 0)
- currBet = 0
- _footermsg, _footercolor = "Dealer wins! Takings: ".._takings, colours.black
- displayBlackjackBets()
- displayBetMessage("LOSE!", colours.red)
- elseif calculateHandValue(hand) == 21 then
- _takings = _takings + math.floor(currBet * 2.5) - insurance
- bank = bank + _takings
- currBet = 0
- _footermsg, _footercolor = "Blackjack! Takings: ".._takings, colours.blue
- displayBlackjackBets()
- displayBetMessage(" BJ! ", colours.blue)
- else
- --The insurance bet, if taken, was lost
- _takings = _takings - insurance
- local _handValue, _handSoft = calculateHandValue(hand)
- drawHandValue(_bx1 - 9, _by + 4, _handValue, _handSoft)
- local _doubledDown = false
- --Double down
- if hand.head.val == hand.head.tail.val then
- _footermsg, _footercolor = "Double Down? Y/N", colours.white
- displayBlackjackBets()
- while true do
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then return end
- if id == "key" and p1 == keys.y then
- _doubledDown = true
- bank = bank - currBet
- currBet = currBet + currBet
- displayBlackjackBets()
- local _c = table.remove(deck, 1)
- _c.x = hand.head.x + styles.width + 1
- _c.y = hand.head.y
- _c.visible = true
- _c. tail = hand.head
- hand.head = _c
- hand:display()
- sleep(0.2)
- break
- elseif id == "key" and p1 == keys.n then
- break
- end
- end
- end
- if not _doubledDown then
- --The player chooses to hit or stay
- local _hitCount = 0
- local _spaceDiff = styles.width + 1
- while true do
- _footermsg, _footercolor = "(H)it or (S)tand? H/S", colours.white
- displayBlackjackBets()
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then return end
- if id == "key" and p1 == keys.h then
- local _c = table.remove(deck, 1)
- _c.x = hand.head.x + _spaceDiff
- _c.y = hand.head.y
- _c.tail = hand.head
- _c.visible = true
- hand.head = _c
- _hitCount = _hitCount + 1
- if _hitCount > 1 and _hitCount < 5 then
- _c = hand.head
- local _shift = _hitCount + 1
- while _c.tail do
- _c:displayTable()
- _c:move(_c.x - _shift, _c.y)
- _shift = _shift - 1
- _c = _c.tail
- end
- _spaceDiff = _spaceDiff - 1
- end
- hand:display()
- _handValue, _handSoft = calculateHandValue(hand)
- drawHandValue(_bx1 - 9, _by + 4, _handValue, _handSoft)
- if _handValue >= 21 then
- sleep(0.2)
- break
- end
- elseif id == "key" and p1 == keys.s then
- sleep(0.2)
- break
- end
- end
- end
- _footermsg = ""
- local _hitCount = 0
- local _spaceDiff = styles.width + 1
- if _handValue <= 21 then
- --The dealer takes his turn to hit or stay
- local _dhandValue, _dhandSoft = calculateHandValue(dealerHand)
- local _c = dealerHand.head
- while _c do _c.visible = true _c = _c.tail end
- dealerHand:display()
- drawHandValue(13, 4, _dhandValue, _dhandSoft)
- sleep(0.2)
- while _dhandValue < 17 and _dhandValue < _handValue do
- local _c = table.remove(deck, 1)
- _c.x = dealerHand.head.x + _spaceDiff
- _c.y = dealerHand.head.y
- _c.visible = true
- _c.tail = dealerHand.head
- dealerHand.head = _c
- _hitCount = _hitCount + 1
- if _hitCount > 1 and _hitCount < 5 then
- _c = dealerHand.head
- local _shift = _hitCount + 1
- while _c.tail do
- _c:displayTable()
- _c:move(_c.x - _shift, _c.y)
- _shift = _shift - 1
- _c = _c.tail
- end
- _spaceDiff = _spaceDiff - 1
- end
- _dhandValue = calculateHandValue(dealerHand)
- dealerHand:display()
- drawHandValue(13, 4, _dhandValue, _dhandSoft)
- sleep(0.4)
- end
- local _betmsg, _betcol = nil, nil
- term.setBackgroundColour(tablebg)
- if _dhandValue > 21 then
- term.setCursorPos(_bx1 + 1, _by + 1)
- _betmsg, _betcol = " WIN!", colours.blue
- _takings = _takings + currBet + currBet - insurance
- _footermsg, _footercolor = "Dealer Bust! Takings: ".._takings, colours.white
- elseif _dhandValue > _handValue then
- term.setCursorPos(_bx1 + 1, _by + 1)
- _betmsg, _betcol = "LOSE!", colours.red
- _takings = _takings - currBet
- _footermsg, _footercolor = "Dealer Wins! Takings: ".._takings, colours.red
- elseif _dhandValue == _handValue then
- term.setCursorPos(_bx1 + 1, _by + 1)
- _takings = currBet
- _betmsg, _betcol = "PUSH!", colours.orange
- _footermsg, _footercolor = "Push! Takings: ".._takings, colours.orange
- else
- term.setCursorPos(_bx1 + 1, _by + 1)
- _takings = _takings + currBet * 2
- _betmsg, _betcol = " WIN!", colours.blue
- _footermsg, _footercolor = "Player Wins! Takings: ".._takings, colours.white
- end
- currBet = 0
- bank = bank + math.max(_takings, 0)
- displayBlackjackBets()
- displayBetMessage(_betmsg,_betcol)
- else
- _takings = _takings - currBet
- currBet = 0
- displayBetMessage("BUST!", colours.red)
- _footermsg, _footercolor = "Player Bust! Takings: ".._takings, colours.red
- displayBlackjackBets()
- end
- end
- --Cleanup
- currBet = 0
- insurance = 0
- splitBet = 0
- end
- --Plays blackjack
- local function playBlackjack()
- xoff = 0
- yoff = 0
- backStyle = styles.backs.alligator
- backbg = colours.red
- backfg = colours.white
- title = "Casino Blackjack"
- startBlackjack()
- local playing = true
- displayBlackjack()
- while playing do
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then return end
- if (id == 'key' and p1 == keys.enter) or
- (id == 'mouse_click' and _bjDeck:clicked(p2,p3)) then
- if bank < tableMin then
- _footermsg, _footercolor = "No money left...", colours.red
- displayBlackjack()
- else
- dealBlackjack()
- handleBlackjackInput()
- end
- end
- playing = not quit and nextGame == nil
- end
- end
- --[[
- Spider Solitaire
- The windows vista classic. Think freecel but more random and harder
- ]]--
- --We use 'spaces' from Klondike in the same fashion
- --The decks of cards that have been completed
- local finishedDecks = {}
- --Difficulty, and the (one or two) suits allowed for use in the game.
- local spiderLevel = 1
- local _suit1, _suit2 = nil, nil
- local _settings = {
- { name = "Easy", decks = "one"; colour = colours.lime; val = 1, y = 4 };
- { name = "Medium", decks = "two"; colour = colours.orange; val = 2, y = 6};
- { name = "Hard", decks = "four", colour = colours.red; val = 3, y = 8};
- }
- --The spider draw deck
- local _spdeck = cardP:new( { x = w - styles.width, y = 2, val = #deck } )
- --Displays the game board for spider
- local function displaySpider()
- term.setBackgroundColour(tablebg)
- term.clear()
- for i=1,#spaces do spaces[i]:display() end
- for i=1,#finishedDecks do finishedDecks[i]:display() end
- _spdeck:display()
- displayOverMenuBar()
- end
- --Performs the initial deal (animated as is now the standard)
- local function dealSpider()
- createDeck(2)
- shuffleDeck()
- _spdeck.val = #deck
- _spdeck:display()
- _suit1, _suit2 = nil, nil
- displaySpider()
- --Changing the suits per difficulty. Standard spider is all/reds/suits but this is a computer!
- if spiderLevel < 3 then
- for i=1,#deck do
- local _c = deck[i]
- if not _suit1 then _suit1 = deck[i].suit
- elseif not _suit2 and spiderLevel == 2 and deck[i].suit.colour ~= _suit1.colour then
- _suit2 = deck[i].suit
- elseif spiderLevel == 1 and deck[i].suit ~= _suit1 then
- deck[i].suit = _suit1
- elseif spiderLevel == 2 and (deck[i].suit ~= _suit1 or deck[i].suit ~= _suit2) then
- if deck[i].suit.colour == _suit1.colour then deck[i].suit = _suit1
- else deck[i].suit = _suit2 end
- end
- end
- end
- --Constructing the 10 spaces
- local _cspace = 1
- for i=1,54 do
- local _c = table.remove(deck, 1)
- _c.x = _cspace + styles.width * (_cspace - 1) + 1
- _c.y = math.ceil(i * 0.1) + 6
- _c.visible = i > 44
- if not spaces[_cspace] then
- spaces[_cspace] = linkedCardP:new()
- spaces[_cspace].head = _c
- else
- _c.tail = spaces[_cspace].head
- spaces[_cspace].head = _c
- end
- _cspace = (_cspace % 10) + 1
- _c:display()
- sleep(0.05)
- end
- end
- --Check to see if a set was made
- local function checkForSet(_deck)
- --We can assume a set can ONLY be made from the head of the deck
- local _val,_topSuit = 1, _deck.head.suit
- local _c = _deck.head
- while _c and _c.val == _val and _c.suit == _topSuit do
- _val = _val + 1
- _c = _c.tail
- end
- --All 13 cards are accounted for
- if _val == 14 then
- table.insert(finishedDecks,linkedCardP:new())
- _c = _deck.head
- _val = 1
- while _c and _c.val == _val and _c.suit == _topSuit do
- _deck.head = _c.tail
- _c.tail = finishedDecks[#finishedDecks].head
- finishedDecks[#finishedDecks].head = _c
- if not _deck.head then
- _deck.head = cardP:new( { x = _c.x, y = _c.y, visible = true, val = 0 } )
- _c:displayTable()
- _c:move(#finishedDecks * 2 + 2, 2)
- _c:display()
- displayOverMenuBar()
- _deck.head:display()
- break
- end
- _c:displayTable()
- _c:move(#finishedDecks * 2 + 2, 2)
- _c = _deck.head
- finishedDecks[#finishedDecks]:display()
- _c:display()
- displayOverMenuBar()
- _val = _val + 1
- sleep(0.2)
- end
- assert(_deck.head ~= nil)
- end
- end
- --Attempts to move one list of cards onto another list of cards
- local function performSpiderMove(_card, _deck)
- --We assume a selection is impossible if wrong-suited cards lie above this one
- if ((selCard.val == _card.val - 1 and _deck.head == _card) or _card.val == 0)
- and selDeck ~= _deck then
- local _top = selDeck.head
- _deck.head = _top
- local _ydiff = 1
- _top:displayTable()
- while _top ~= selCard do
- _top = _top.tail
- _ydiff = _ydiff + 1
- _top:displayTable()
- end
- selDeck.head = selCard.tail
- if selDeck.head == nil then
- selDeck.head = cardP:new( { val = 0; x = selCard.x; y = selCard.y; } )
- end
- selDeck:display()
- if _card.val == 0 then
- selCard.tail = nil
- _deck.head:move(_card.x, _card.y + _ydiff - 1, true)
- else
- selCard.tail = nil
- _deck.head:move(_card.x, _card.y + _ydiff, true)
- selCard.tail = _card
- end
- select()
- _deck:display()
- checkForSet(_deck)
- else
- local _c = selDeck
- select(_card, _deck)
- _c:display()
- _deck:display()
- end
- end
- --Draws 10 more cards from the deck
- local function dealSpiderDeck()
- local _canSkip, _shouldSkip = false, false
- for i=1,#spaces do
- if spaces[i].head.tail then _canSkip = true end
- if spaces[i].head.val == 0 then _shouldSkip = true end
- end
- if _canSkip and _shouldSkip then return end
- for i=1,#spaces do
- local _c = table.remove(deck, 1)
- _spdeck.val = #deck
- _c.visible = true
- _c.tail = spaces[i].head
- _c.x = spaces[i].head.x
- _c.y = spaces[i].head.y + 1
- spaces[i].head = _c
- spaces[i]:display()
- _spdeck:display()
- displayOverMenuBar()
- sleep(0.1)
- end
- for i=1,#spaces do checkForSet(spaces[i]) end
- end
- --Handles basic spider input and events
- local function handleSpiderInput()
- local gameWon = false
- while not gameWon do
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then
- return
- end
- if id == "mouse_click" then
- --Check if the deck was clicked
- if _spdeck:clicked(p2,p3) and _spdeck.val > 0 then
- dealSpiderDeck()
- else
- local _complete = false
- for i=1,#spaces do
- local _c = spaces[i].head
- local _topSuit = _c.suit
- while _c do
- if _c:clicked(p2,p3) then
- if _c.val == 0 and selCard then
- performSpiderMove(_c, spaces[i])
- elseif _c.visible then
- if not selCard or selDeck == spaces[i] then
- local _d = selDeck
- select(_c, spaces[i])
- if _d then _d:display() end
- selDeck:display()
- elseif _c == selCard then
- break;
- elseif _c == spaces[i].head then
- performSpiderMove(_c, spaces[i])
- end
- elseif not _c.visible and _c == spaces[i].head then
- _c.visible = true
- _c:display()
- local _d = selDeck
- select()
- if _d then _d:display() end
- else
- local _d = selDeck
- select()
- if _d then _d:display() end
- end
- _complete = true
- break
- end
- if not _c.tail or _c.tail.suit ~= _topSuit or _c.tail.val ~= _c.val + 1 then
- break
- end
- _c = _c.tail
- end
- if _complete then break end
- end
- if not _complete then
- local _d = selDeck
- select()
- if _d then _d:display() end
- end
- lmX, lmY = p2, p3
- txoff, tyoff = xoff, yoff
- displayOverMenuBar()
- gameWon = #finishedDecks == 8
- end
- elseif id == "mouse_drag" then
- if lmX > 0 and lmY > 0 then
- local yDiff = p3 - lmY
- --Mouse drag events seem to be pretty overzealous. Or is that a version issue...
- if tyoff + yDiff ~= yoff then
- yoff = math.min(tyoff + yDiff, 0)
- --h-21 is the maximum unrevealed column (6) plus the max revealed column (13)
- --plus 3 to reveal the face of the card and 4 more for whitespace
- yoff = math.max(yoff, h - 30)
- displaySpider()
- end
- end
- elseif id == "mouse_scroll" then
- yoff = math.min(yoff - p1, 0)
- yoff = math.max(yoff, h - 30)
- displaySpider()
- --Check each space on the board
- elseif id == "key" then
- if p1 == keys.down then
- yoff = math.min(yoff + 1, 0)
- displaySpider()
- elseif p1 == keys.up then
- yoff = math.max(yoff - 1, h - 30)
- displaySpider()
- end
- end
- end
- --A final win animation
- local _t = {
- { x = math.floor(w/2); y = math.floor(h/2); xdir = -1; ydir = -1; };
- { x = math.floor(w/2) + 1; y = math.floor(h/2); xdir = 1; ydir = -1; };
- { x = math.floor(w/2); y = math.floor(h/2) + 1; xdir = -1; ydir = -0.25; };
- { x = math.floor(w/2) + 1; y = math.floor(h/2) + 1; xdir = 1; ydir = -0.25; };
- { x = math.floor(w/2); y = math.floor(h/2) + 2; xdir = -1; ydir = 0.25; };
- { x = math.floor(w/2) + 1; y = math.floor(h/2) + 2; xdir = 1; ydir = 0.25; };
- { x = math.floor(w/2); y = math.floor(h/2) + 3; xdir = -1; ydir = 1; };
- { x = math.floor(w/2) + 1; y = math.floor(h/2) + 3; xdir = 1; ydir = 1; };
- }
- for i = 1, #finishedDecks do
- for j = 1, #_t do
- term.setCursorPos(_t[j].x, _t[j].y)
- term.setBackgroundColour(tablebg)
- term.setTextColour(finishedDecks[i].head.suit.colour)
- term.write(finishedDecks[i].head.suit.char)
- _t[j].x = _t[j].x + _t[j].xdir
- _t[j].y = _t[j].y + _t[j].ydir
- end
- displayOverMenuBar()
- sleep(0.2)
- end
- end
- --Chooses easy medium or hard
- local function chooseDifficuly()
- for i=1,#_settings do
- local _s = _settings[i]
- term.setCursorPos(10, _s.y)
- term.setBackgroundColour(colours.grey)
- term.setTextColour(_s.colour)
- term.write(" ".._s.name.." ")
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.white)
- term.setCursorPos(20, _s.y)
- term.write(_s.decks.." suit(s)")
- end
- while true do
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then return false end
- if id == "mouse_click" then
- for i=1,#_settings do
- if p2 >= 10 and p2 <= 25 and _settings[i].y == p3 then
- spiderLevel = _settings[i].val
- return true
- end
- end
- end
- end
- end
- --Deals up a good old fashion game of spider
- local function playSpider()
- xoff = 0
- yoff = 0
- backbg = colours.purple
- backfg = colours.black
- backStyle = styles.backs.spooky
- title = "Spider Solitaire"
- spaces = {}
- finishedDecks = {}
- _spdeck.val = 1
- displaySpider()
- if not chooseDifficuly() then return end
- displaySpider()
- dealSpider()
- handleSpiderInput()
- end
- --[[
- Golf
- Because apparently I'm enjoying making these card games
- ]]--
- --Like spider and klondike, this uses the spaces deck (7 slots like klondike
- --The card clicked to draw another card to the tableau
- local _gfcard = cardP:new( {
- x = w/2 - styles.width / 2 - styles.width + 1;
- y = h - styles.height - 1;
- visible = false;
- val = 1;
- } )
- --The foundation
- local foundation = linkedCardP:new()
- --Makes sure golf is ready for the next game
- local function setupGolf()
- createDeck(1)
- shuffleDeck()
- _gfcard.val = #deck
- foundaction = linkedCardP:new()
- foundation.head = cardP:new({
- x = _gfcard.x + 2 + styles.width;
- y = _gfcard.y;
- visible = false;
- val = 0;
- })
- local _extraSpace = (w - (styles.width * 7)) / 8
- for i=1,7 do
- spaces[i] = linkedCardP:new()
- spaces[i].head = cardP:new({
- x = _extraSpace * i + styles.width * (i - 1) + 1;
- y = 3;
- visible = false;
- val = 0;
- })
- end
- end
- --Gets the score of a game of golf
- local function getGolfScore()
- local _score = 0
- for i=1,#spaces do
- local _c = spaces[i].head
- while _c and _c.val ~= 0 do
- _c = _c.tail
- _score = _score + 1
- end
- end
- if _score == 0 then _score = -#deck end
- return _score
- end
- --Displays golf
- local function displayGolf()
- term.setBackgroundColour(tablebg)
- term.clear()
- for i=1,#spaces do if spaces[i] then spaces[i]:display() else break end end
- _gfcard:display()
- foundation:display()
- displayOverMenuBar()
- term.setCursorPos(3,h)
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.black)
- local _score = getGolfScore()
- if _score < -15 then _score = 0 end
- term.write("Score: ".._score.." ")
- end
- --Deals up a hand of golf
- local function dealGolf()
- local _index = 1
- for i=1,35 do
- local _c = table.remove(deck, 1)
- _c.visible = true
- _c.x = spaces[_index].head.x
- if spaces[_index].head.val == 0 then
- _c.y = spaces[_index].head.y
- else
- _c.y = spaces[_index].head.y + 1
- end
- _c.tail = spaces[_index].head
- spaces[_index].head = _c
- _c:display()
- sleep(0.1)
- _index = _index % 7 + 1
- end
- displayGolf()
- end
- --Attempts to move a card from the tabluea to the foundation
- local function tryGolfMove(_deck)
- if foundation.head.val == 13 or _deck.head.val == 0 or foundation.head.val == 0 then
- return false
- end
- local scoreDiff = foundation.head.val - _deck.head.val
- if math.abs(scoreDiff) == 1 then
- local _c = _deck.head
- _deck.head = _c.tail
- _c:displayTable()
- _c:move(foundation.head.x, foundation.head.y)
- _c.tail = foundation.head
- foundation.head = _c
- _deck:display()
- foundation:display()
- return true
- else return false end
- end
- --Handles all that golfing you'll be doing
- local function handleGolfInput()
- local _currScore = getGolfScore()
- while _currScore > 0 do
- local id,p1,p2,p3 = os.pullEvent()
- if manageMenuMouseEvents(id,p2,p3) then return end
- if id == "mouse_click" then
- if _gfcard.val > 0 and _gfcard:clicked(p2,p3) then
- _c = table.remove(deck, 1)
- _c.x = foundation.head.x
- _c.y = foundation.head.y
- _c.visible = true
- _c.tail = foundation.head
- foundation.head = _c
- _gfcard.val = #deck
- foundation:display()
- _gfcard:display()
- else
- for i=1,#spaces do
- local _c = spaces[i].head
- --Moving from space to the foundation
- if _c:clicked(p2,p3) then
- if tryGolfMove(spaces[i]) then
- _currScore = getGolfScore()
- term.setCursorPos(3,h)
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.black)
- term.write("Score: "..getGolfScore().." ")
- end
- end
- end
- end
- end
- end
- displayGolf()
- term.setBackgroundColour(tablebg)
- term.setTextColour(colours.black)
- term.setCursorPos(_gfcard.x + 2, _gfcard.y - 3)
- term.write("/'")
- term.setCursorPos(_gfcard.x, _gfcard.y - 2)
- term.write("o/")
- end
- local function playGolf()
- xoff = 0
- yoff = 0
- backbg = colours.yellow
- backfg = colours.blue
- backStyle = styles.backs.royal
- title = "Golf"
- setupGolf()
- displayGolf()
- dealGolf()
- handleGolfInput()
- end
- --[[
- Game Management Stuff
- ]]--
- table.insert(menuOptions, { name = "Klondike", key = "K", enabled = true, value = function()
- currOverDraw = drawKlondike
- nextGame = playKlondike
- end })
- table.insert(menuOptions, { name = "Blackjack", key = "B", enabled = true, value = function()
- currOverDraw = displayBlackjack
- nextGame = playBlackjack
- end })
- table.insert(menuOptions, { name = "Spider", key = "S", enabled = true, value = function()
- currOverDraw = displaySpider
- nextGame = playSpider
- end })
- table.insert(menuOptions, { name = "Golf", key = "G", enabled = true, value = function()
- currOverDraw = displayGolf
- nextGame = playGolf
- end })
- table.insert(menuOptions, { name = "--------", enabled = false, value = nil })
- table.insert(menuOptions, { name = "Quit", key = "Q", enabled = true, value = function()
- currOverDraw = nil
- nextGame = nil
- quit = true
- end })
- if not term.isColor() then print("For advanced computers only") return end
- local function displayNitrosoftTitle()
- shell.run("clear")
- local _w,_h = term.getSize()
- local _t1,_t2 = "nitrosoft", "games"
- term.setCursorPos(math.floor(_w/2-#_t1), math.floor(_h/2))
- if term.isColour() then term.setTextColour(colours.blue) end
- term.write(_t1)
- term.setCursorPos(math.floor(_w/2-#_t2/2),math.floor(_h/2)+1)
- term.setTextColour(colours.white)
- term.write(_t2)
- term.setCursorPos(math.floor(_w/2-#_t1), math.floor(_h/2)+2)
- if term.isColour() then term.setTextColour(colours.red) end
- term.write(string.rep("-",#_t1*2))
- if term.isColour() then
- term.setBackgroundColour(colours.green)
- term.setCursorPos(_w/2+#_t1-4, math.floor(_h/2)-2)
- term.write(" ")
- term.setCursorPos(_w/2+#_t1-4, math.floor(_h/2)-1)
- term.write(" ")
- term.setBackgroundColour(colours.white)
- term.setTextColour(colours.red)
- term.setCursorPos(_w/2+#_t1-3, math.floor(_h/2)-1)
- term.write("4 ")
- term.setCursorPos(_w/2+#_t1-3, math.floor(_h/2))
- term.write(" v")
- end
- term.setBackgroundColour(colours.black)
- term.setTextColour(colours.white)
- term.setCursorPos(1,_h)
- os.pullEvent("key")
- end
- displayNitrosoftTitle()
- --The over menu game fnction
- local function overMenuInput()
- term.setBackgroundColour(tablebg)
- term.clear()
- displayOverMenuBar()
- while not quit do
- if nextGame then
- local _game = nextGame
- nextGame = nil
- _game()
- else
- local id,p1,p2,p3 = os.pullEvent()
- manageMenuMouseEvents(id,p2,p3)
- end
- end
- end
- overMenuInput()
- term.setBackgroundColour(colours.black)
- shell.run("clear")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement