packages feed

showdown-0.5.3: Showdown.hs

module Showdown where

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
import Graphics.UI.Gtk.Gdk.Events
import Control.Monad
import Data.IORef
import System.Random

data State = State {
      p1    :: String
    , p2    :: String
    , ptext :: String
    , cyls  :: Int
    , curp  :: Int
    , mnum  :: Int } deriving (Show)

data Player = Player1 | Player2
            deriving (Eq)

main :: FilePath -> IO ()
main gladepath = do
  initGUI
  -- Create inital game state
  st <- newIORef (State "" "" "" 0 0 0)
  -- Get handles to all needed widgets from Glade file
  Just xml <- xmlNew gladepath
  mw     <- xmlGetWidget xml castToWindow "mainWindow"
  sw     <- xmlGetWidget xml castToWindow "window2"
  mb     <- xmlGetWidget xml castToButton "fireButton"
  ng     <- xmlGetWidget xml castToButton "nbutton"
  mv     <- xmlGetWidget xml castToTextView "textview1"
  but    <- xmlGetWidget xml castToButton "button1"
  ab     <- xmlGetWidget xml castToAboutDialog "aboutdialog1"
  menu   <- xmlGetWidget xml castToMenuItem "menuitem3"
  about  <- xmlGetWidget xml castToMenuItem "imagemenuitem10"
  quitb  <- xmlGetWidget xml castToMenuItem "menuitem3"
  p1name <- xmlGetWidget xml castToEntry "entry1"
  p2name <- xmlGetWidget xml castToEntry "entry2"
  spin   <- xmlGetWidget xml castToSpinButton "spinbutton1"
  sett   <- xmlGetWidget xml castToMenuItem "menuitem2"
  p1lab  <- xmlGetWidget xml castToLabel "label4"
  p2lab  <- xmlGetWidget xml castToLabel "label5"
  cylab  <- xmlGetWidget xml castToLabel "label6"
  -- Handle events and such
  buff <- textViewGetBuffer mv
  onActivateLeaf quitb mainQuit
  onKeyPress mv $ \(Key { eventKeyName = name }) -> if name == "Return"
                                                    then fireButton st sw buff cylab mv >>
                                                         return True
                                                    else return False
  onResponse ab $ \resp -> 
     case resp of
        ResponseClose -> widgetHide ab
        _             -> widgetHide ab
  onActivateLeaf about $ widgetShow ab
  onActivateLeaf sett  $ widgetShowAll sw
  onDelete sw $ \_ -> widgetHide sw >> return True
  onClicked but $ 
            initalize sw p1name p2name spin st p1lab p2lab cylab buff
  onClicked ng $ widgetShowAll sw
  onDelete ab $ \_ -> widgetHide ab >> return True
  onClicked mb $ fireButton st sw buff cylab mv
  onActivateLeaf menu $ widgetDestroy mw
  onDestroy mw mainQuit
  widgetShowAll mw
  mainGUI

initalize :: Window  -> Entry -> Entry -> SpinButton
          -> IORef State -> Label -> Label -> Label 
          -> TextBuffer -> IO ()
-- Initalize the game
initalize w p1n p2n sb st p1l p2l cyl buf = do
  widgetHide w
  p1   <- entryGetText p1n
  p2   <- entryGetText p2n
  cyls <- liftM truncate $ get sb spinButtonValue
  magn <- randomRIO (1,cyls)
  writeIORef st $ State p1 p2 "" cyls 0 magn
  updateLabels p1 p2 cyls p1l p2l cyl
  textBufferSetText buf ""

fireButton :: IORef State 
            -> Window 
            -> TextBuffer 
            -> Label
            -> TextView
            -> IO ()
-- Guts of the program
fireButton st sw buff cylab tv= do
  state <- readIORef st
  if (cyls state == 0 || cyls state == ((mnum state) -1)) 
   then widgetShowAll sw
   else do
     textBufferSetText buff $ 
                       game (ptext state) (cyls state) (mnum state) (currentP  (p1 state) (p2 state) (curp state))
     si <- textBufferGetStartIter buff
     ei <- textBufferGetEndIter buff
     text <- textBufferGetText buff si ei False
     textViewScrollToIter tv ei 0 Nothing
     writeIORef st $ state { cyls = ((cyls state) -1), curp = (curp state + 1), ptext = text }
     labelSetText cylab $ "Cylinders remaining: " ++ show ((cyls state) -1)

-- Figures out the current player based on whether or not
-- n is even.
currentP :: String -> String -> Int -> String
currentP p1 p2 n = if (p1OrP2 n == Player1) then p1 else p2

p1OrP2 :: Int -> Player
p1OrP2 n = if (even n) then Player1 else Player2

-- Returns the game message appropriate for the current
-- round.
game :: String -> Int -> Int -> String -> String
game pt cyl mn pname | cyl == mn  = pt        ++
                                    "Bang! "  ++ 
                                    pname     ++ " bit the bullet!\n"
                     | otherwise  = pt        ++
                                    "Click! " ++ 
                                    pname     ++ " lives another round!\n"

--Updates a bunch-o-labels
updateLabels :: String 
             -> String 
             -> Int 
             -> Label 
             -> Label  
             -> Label 
             -> IO ()

updateLabels p1 p2 cyl p1l p2l cyll = do
  setPlayer 1 p1 p1l
  setPlayer 2 p2 p2l
  labelSetText cyll $ "Cylinders Remaining: " ++ (show cyl)

-- Sets the label for each player
setPlayer :: Int -> String -> Label -> IO ()
setPlayer pn str lab
              | pn == 1   = setlab "Player 1: "
              | otherwise = setlab "Player 2: "
              where setlab x = labelSetText lab (x ++ str)