Pages

Sunday, August 28, 2011

jablayUNO.tcl

# uno.tcl

# Default settings
set UnoAds 0
set UnoDebug 0
set UnoChan "#rhapsody"
set UnoRobot $botnick
set UnoPointsName "Keong"
set UnoStopAfter 3
set UnoBonus 1000
set UnoWildDrawTwos 0
set UnoCFGFile "sigit/uno.cfg"
set UnoScoreFile "sigit/uno.scores"
set UnoMaxNickLen 9
set UnoMaxPlayers 10
set UnoOpFlags "o|o"
set UnoNTC "NOTICE"
set UnoVersion "1.00"

# Command binds
bind pub - !unocmds UnoCmds
bind pub - .remove UnoRemove
bind pub - !pause UnoPause
bind pub - !unowon UnoWon
bind pub - !unotop10 UnoTopTen
bind pub - !unotop3last UnoTopThreeLast
bind pub - !unostats UnoPlayStats
bind pub - !unorecords UnoRecords
bind pub - !unorow UnoCurrentRow
bind pub - !unoversion UnoVersion
bind pub - !uno UnoInit
bind pub - .suno UnoStop

# DCC commands
bind dcc - unohands dccunohands
bind dcc - unorehash dcc_unorehash

# Monthly score reset
bind time - "00 00 01 * *" UnoNewMonth

# Global variables
set UnoOn 0
set UnoMode 0
set UnoPaused 0
set UnoPlayers 0
set MasterDeck ""
set UnoDeck ""
set DiscardPile ""
set PlayCard ""
set RoundRobin ""
set ThisPlayer ""
set ThisPlayerIDX 0
set UnoStartTime [unixtime]
set IsColorChange 0
set ColorPicker ""
set IsDraw 0
set UnoIDX ""
set UnPlayedRounds 0
set UnoWinDefault 0
set UnoLastWinner ""
set UnoWinsInARow 0

# Scores, records and ads
set UnoLastMonthCards(0) "Nobody 0"
set UnoLastMonthCards(1) "Nobody 0"
set UnoLastMonthCards(2) "Nobody 0"
set UnoLastMonthGames(0) "Nobody 0"
set UnoLastMonthGames(1) "Nobody 0"
set UnoLastMonthGames(2) "Nobody 0"
set UnoFast "Nobody 600"
set UnoHigh "Nobody 0"
set UnoPlayed "Nobody 0"
set UnoRow "Nobody 0"
set UnoRecordHigh "Nobody 0"
set UnoRecordFast "Nobody 600"
set UnoRecordCard "Nobody 0"
set UnoRecordWins "Nobody 0"
set UnoRecordPlayed "Nobody 0"
set UnoRecordRow "Nobody 0"
set UnoAdNumber 0

# Card stats
set CardStats(played) 0
set CardStats(wilds) 0
set CardStats(draws) 0
set CardStats(skips) 0

# Timers
set UnoStartTimer ""
set UnoSkipTimer ""
set UnoCycleTimer ""
set UnoBotTimer ""

# Grace periods and timeouts
# AutoSkipPeriod can be raised but no lower than 2
set AutoSkipPeriod 2
set StartGracePeriod 30
set RobotRestartPeriod 1
set UnoCycleTime 30

# Cards and logo
set UnoRedCard "\0030,04 \002R\002ed "
set UnoGreenCard "\0030,03 \002G\002reen "
set UnoBlueCard "\0030,12 \002B\002lue "
set UnoYellowCard "\0031,08 \002Y\002ellow "
set UnoSkipCard "\002S\002kip \003 "
set UnoReverseCard "\002R\002everse \003 "
set UnoDrawTwoCard "\002D\002raw \002T\002wo \003 "
set UnoWildCard "\0031,8\002 W \0030,3 I \0030,4 L \0030,12 D \002\003 "
set UnoWildDrawFourCard "\0031,8\002 W \0030,3I\0030,4L\0030,12D\002\0031,8\002 D \002\0030,3r\0030,4a\0030,12w\0031,8\002 F \002\0030,3o\0030,4u\0030,12r \003 "
set UnoLogo "6Jablay3U12N13O04!00 "

set UGood [list "Keren" "Cerdik" "Cakep" "Sip" "Oke" "Benar" "Betul" "Tepat" "Seep" "Pinter" "Mantap" "Oce" "Yup" "Yap" "Yess" "Yoi" "Oyee" "Waw" "Begh" "Jah" "ew"]
set UBad [list "Euh payah!" "Payah nich!" "Cape deh!" "Gak pinter ah!" "Ndak cerdik ah!" "Dudung ah"]
set UDraw [list "Slurp!" "Gluk!" "Glek!" "Gluguk!" "Glegek!" "Cruts" "Crotz"]

#
# Bind channel commands 
#
proc UnoBindCmds {} {
 bind pub - jo UnoJoin
 bind pub - od UnoOrder
 bind pub - ti UnoTime
 bind pub - ca UnoShowCards
 bind pub - pl UnoPlayCard
 bind pub - cd UnoTopCard
 bind pub - tu UnoTurn
 bind pub - dr UnoDraw
 bind pub - co UnoColorChange
 bind pub - pa UnoPass
 bind pub - ct UnoCardCount
 bind pub - st UnoCardStats
 bind chon - * unologin:dcc
 bind chof - * unologout:dcc
 bind filt - .quit* unologout:filt
}

#
# Unbind channel commands 
#
proc UnoUnbindCmds {} {
 catch {unbind pub - jo UnoJoin}
 catch {unbind pub - od UnoOrder}
 catch {unbind pub - ti UnoTime}
 catch {unbind pub - ca UnoShowCards}
 catch {unbind pub - pl UnoPlayCard}
 catch {unbind pub - cd UnoTopCard}
 catch {unbind pub - tu UnoTurn}
 catch {unbind pub - dr UnoDraw}
 catch {unbind pub - co UnoColorChange}
 catch {unbind pub - pa UnoPass}
 catch {unbind pub - ct UnoCardCount}
 catch {unbind pub - st UnoCardStats}
 catch {unbind chon - * unologin:dcc}
 catch {unbind chof - * unologout:dcc}
 catch {unbind filt - .quit* unologout:filt}
}

#
# Reset game variables
#
proc UnoReset {} {
 global UnoOn UnoMode UnoPaused UnoPlayers RoundRobin UnoDeck ThisPlayer ThisPlayerIDX PlayCard
 global DiscardPile IsColorChange ColorPicker IsDraw UnoIDX MasterDeck CardStats
 global UnoStartTimer UnoSkipTimer UnoCycleTimer UnoWinDefault UnoRobot botnick

 set UnoMode 0
 set UnoPaused 0
 set UnoPlayers 0
 set MasterDeck ""
 set UnoDeck ""
 set DiscardPile ""
 set RoundRobin ""
 set ThisPlayer ""
 set ThisPlayerIDX 0
 set PlayCard ""
 set IsColorChange 0
 set ColorPicker ""
 set IsDraw 0
 set UnoIDX ""
 set UnoAdNumber 0
 set UnoWinDefault 0

 set CardStats(played) 0
 set CardStats(wilds) 0
 set CardStats(draws) 0
 set CardStats(skips) 0

 set UnoStartTimer ""
 set UnoSkipTimer ""
 set UnoCycleTimer ""

 set UnoRobot $botnick
 return
}

#
# is this the uno channel?
#
proc uno_ischan {chan} {
 global UnoChan
 if {($chan == $UnoChan)} {return 1}
 return 0
}

#
# Stop a game
#
proc UnoStop {nick uhost hand chan txt} {
 global UnoOn UnoPaused UnPlayedRounds UnoStartTimer UnoSkipTimer UnoCycleTimer RoundRobin UnoDCCIDX
 global UnoLastWinner UnoWinsInARow UnoLogo UnoRobot

 if {(![uno_ischan $chan])||($UnoOn == 0)} {return}
 if {$nick!=$UnoRobot} {
  if {[isop $nick $chan]==0} {
  unochanmsg "\00314oit..tidak bisa yang bisa stop hanya\00305OP"
  return
}
 }

 catch {killutimer $UnoStartTimer}
 catch {killtimer $UnoSkipTimer}
 catch {killutimer $UnoCycleTimer}

 # remove player dcc list
 set pcount 0
 while {[lindex $RoundRobin $pcount] != ""} {
  set pnick [lindex $RoundRobin $pcount]
  if [info exist UnoDCCIDX($pnick)] {unset UnoDCCIDX($pnick)}
  incr pcount
 }

 unochanmsg "\00314dihentikan oleh \00305$nick"

 set UnoOn 0
 set UnoPaused 0
 set UnPlayedRounds 0
 set UnoLastWinner ""
 set UnoWinsInARow 0

 UnoUnbindCmds
 UnoReset

 return
}

#
# First entry
#
proc UnoInit {nick uhost hand chan txt} {
 global UnoOn
 if {(![uno_ischan $chan])||($UnoOn > 0)} {return}
 unochanmsg "\00314dimulai oleh \00305$nick"
 set UnoOn 1
 UnoBindCmds
 UnoNext
 return
}

#
# Initialize a new game
#
proc UnoNext {} {
 global UnoOn MasterDeck UnoDeck UnoMode StartGracePeriod UnoHand UnoVersion UnoStartTimer UnoSkipTimer

 if {!$UnoOn} {return}

 UnoReset

 set UnoMode 1

 set MasterDeck [list B0 B1 B1 B2 B2 B3 B3 B4 B4 B5 B5 B6 B6 B7 B7 B8 B8 B9 B9 BR BR BS BS BD BD R0 R1 R1 R2 R2 R3 R3 R4 R4 R5 R5 R6 R6 R7 R7 R8 R8 R9 R9 RR RR RS RS RD RD Y0 Y1 Y1 Y2 Y2 Y3 Y3 Y4 Y4 Y5 Y5 Y6 Y6 Y7 Y7 Y8 Y8 Y9 Y9 YR YR YS YS YD YD G0 G1 G1 G2 G2 G3 G3 G4 G4 G5 G5 G6 G6 G7 G7 G8 G8 G9 G9 GR GR GS GS GD GD W W W W WD WD WD WD]

 set rseed [clock format [clock seconds] -format "%s"]
 set newrand [expr srand($rseed)]
 set newrand [rand [llength $MasterDeck]]

 set UnoDeck ""
 while {[llength $UnoDeck] != 108} {
  set pnum [rand [llength $MasterDeck]]
  set pcard [lindex $MasterDeck $pnum]
  lappend UnoDeck $pcard
  set MasterDeck [lreplace $MasterDeck $pnum $pnum]
 }

 if [info exist UnoHand] {unset UnoHand}
 
 unochanmsg "\00314kamu punya waktu \00303[UnoDuration $StartGracePeriod] \00314untuk join"

 set UnoStartTimer [utimer $StartGracePeriod UnoStart]

 return
}

#
# Cycle a new game
#
proc UnoCycle {} {
 global UnoOn UnoMode UnoCycleTime UnoCycleTimer UnoSkipTimer UnoAds

 if {!$UnoOn} {return}

 set UnoMode 4
 catch {killtimer $UnoSkipTimer}

 if {$UnoAds>0} {
  set AdTime [expr $UnoCycleTime /2]
  set UnoAdTimer [utimer $AdTime UnoScoreAdvertise]
 }

 set UnoCycleTimer [utimer $UnoCycleTime UnoNext]

 return
}

#
# Start a new game
#
proc UnoStart {} {
 global UnoChan UnoOn UnoCycleTime UnoRobot UnoDebug UnoIDX UnoStartTime UnoPlayers RoundRobin ThisPlayer ThisPlayerIDX UnoDeck DiscardPile UnoMode UnoHand PlayCard AutoSkipPeriod
 global UnoSkipTimer UnPlayedRounds UnoStopAfter UnoLogo

 if {!$UnoOn} {return}

 if {![llength $RoundRobin]} {
  unochanmsg "\00314ga ada yang maen..., ronde berikutnya dimulai \00303[UnoDuration $UnoCycleTime] \00314lagi"
  incr UnPlayedRounds
  if {($UnoStopAfter > 0)&&($UnPlayedRounds >= $UnoStopAfter)} {
    unochanmsg "\00314dihentikan... \(gak ada pemaen setelah \00303$UnoStopAfter \00314ronde\)"
    utimer 1 "UnoStop $UnoRobot $UnoRobot none $UnoChan none"
    return
  }

  UnoCycle

  return
 }

 # bot joins if one player
 if {[llength $RoundRobin] == 1} {
  incr UnoPlayers

  lappend RoundRobin $UnoRobot
  lappend UnoIDX $UnoRobot

  if [info exist UnoHand($UnoRobot)] {unset UnoHand($UnoRobot)}

  set UnoHand($UnoRobot) ""

  unomsg "\00305$UnoRobot \00314joins $UnoLogo"

  # deal hand to bot
  UnoNewPlayerHand $UnoRobot

  if {$UnoDebug > 1} {unolog $UnoRobot $UnoHand($UnoRobot)}
 }

 unomsg "\00314Selamat Bermain $UnoLogo"
 unomsg "\00314ada \00303$UnoPlayers \00303pemain, \00314yaitu: \00305$RoundRobin"
 set UnoMode 2

 set ThisPlayer [lindex $RoundRobin 0]

 # draw first card from deck
 set DiscardPile ""
 set pcardnum [rand [llength $UnoDeck]]
 set pcard [lindex $UnoDeck $pcardnum]

 # play doesnt start with a wild card
 while {[string range $pcard 0 0] == "W"} {
  set pcardnum [rand [llength $UnoDeck]]
  set pcard [lindex $UnoDeck $pcardnum]
 }

 set PlayCard $pcard
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 set UnoDeck [lreplace $UnoDeck $pcardnum $pcardnum]

 # first player draws two if first card is a draw two, but not skipped
 unomsg "\00305$ThisPlayer \00314dapat giliran pertama... Kartu: $Card"

 if {([string range $pcard 0 0] != "W")&&([string range $pcard 1 1] == "D")} {
   unomsg "\00305$ThisPlayer \00314ngambil\00303 2 \00314cards"
   UnoAddDrawToHand $ThisPlayer $ThisPlayerIDX 2
 }

 uno_showcards $ThisPlayer $ThisPlayerIDX

 # start autoskip timer
 set UnoSkipTimer [timer $AutoSkipPeriod UnoAutoSkip]

 set UnPlayedRounds 0

 # running game time
 set UnoStartTime [unixtime]

 return
}

#
# Add a player
#
proc UnoJoin {nick uhost hand chan txt} {
 global UnoDebug UnoIDX UnoMode UnoPlayers RoundRobin UnoHand UnoMaxPlayers UnoDCCIDX UnoLogo

 if {(![uno_ischan $chan])||($UnoMode < 1)||($UnoMode > 2)} {return}

 # player is already joined
 set pcount 0
 while {[lindex $RoundRobin $pcount] != ""} {
  if {[lindex $RoundRobin $pcount] == $nick} {
   return
  }
  incr pcount
 }

 if {[llength $RoundRobin] >= $UnoMaxPlayers} {
  unogntc $nick "$UnoLogo dibatasi sampai $UnoMaxPlayers pemaen... coba lagi nanti, \00305$nick"
  return
 }

 incr UnoPlayers

 lappend RoundRobin $nick
 lappend UnoIDX $nick

 if [info exist UnoHand($nick)] {unset UnoHand($nick)}
 if [info exist UnoDCCIDX($nick)] {unset UnoDCCIDX($nick)}

 set UnoHand($nick) ""
 
 # if player is in dcc chat, use that socket for card output (fast)
 set UnoDCCIDX($nick) -1

 set dhand [nick2hand $nick $chan] 

 if {($dhand != "")&&($dhand != "*")} {
  set idx [hand2idx $dhand]
  if {$idx != -1} {
   set UnoDCCIDX($nick) $idx
  } {
   set UnoDCCIDX($nick) -1
  }
 }

 # deal hand
 UnoNewPlayerHand $nick

 set Card [UnoCardColorAll $nick]

 #if {$UnoDebug > 1} { unolog $nick $UnoHand($nick) }

 unomsg "\00305$nick \00314join $UnoLogo"

 unontc $nick "Kartumu: $Card"
 unontc $nick "Gunakan \00304!unocmds\003 untuk melihat daftar perintah yang ada"
 return
}

#
# Deal full hand of 7 cards
#
proc UnoNewPlayerHand {cplayer} {
 global UnoDeck UnoHand
 # re-shuffle deck if needed
 UnoShuffle 7
 # deal cards to player
 while {[llength $UnoHand($cplayer)] != 7} {
  set picknum [rand [llength $UnoDeck]]
  set pick [lindex $UnoDeck $picknum]
  set UnoDeck [lreplace $UnoDeck $picknum $picknum]
  lappend UnoHand($cplayer) $pick
 }
}

#
# Add drawn cards to hand
#
proc UnoAddDrawToHand {cplayer idx num} {
 global UnoHand UnoDeck RoundRobin CardStats

 # check if deck needs reshuffling
 UnoShuffle $num

 set newhand [expr [llength $UnoHand($cplayer)] + $num]

 set Card ""
 while {[llength $UnoHand($cplayer)] != $newhand} {
  set pcardnum [rand [llength $UnoDeck]]
  set pcard [lindex $UnoDeck $pcardnum]
  set UnoDeck [lreplace $UnoDeck $pcardnum $pcardnum]
  lappend UnoHand($cplayer) $pcard
  append Card [UnoCardColor $pcard]
 }

 showdraw $idx $Card
}

#
# Remove played card from hand
#
proc UnoRemoveCardFromHand {cplayer ccard} {
 global UnoHand
 set UnoHand($cplayer) [lreplace $UnoHand($cplayer) $ccard $ccard]
}

#
# Add card to discard pile
#
proc UnoAddToDiscardPile {ccard} {
 global DiscardPile
 if {[string range $ccard 0 0] != ""} { lappend DiscardPile $ccard }
}

#
# Draw a card
#
proc UnoDraw {nick uhost hand chan txt} {
 global UnoMode UnoDeck ThisPlayer ThisPlayerIDX UnoHand RoundRobin IsDraw CardStats

 if {(![uno_ischan $chan])||($UnoMode != 2)||($nick != $ThisPlayer)} {return}

 UnoAutoSkipReset

 if {$IsDraw>0} {
  unontc $nick "Kan udah ngambil tadi, $nick, mainkan kartu lain ato pass saja"
  return
 }

 set IsDraw 1

 UnoAddDrawToHand $ThisPlayer $ThisPlayerIDX 1

 showwhodrew $nick

 return
}

#
# Pass a turn
#
proc UnoPass {nick uhost hand chan txt} {
 global UnoMode ThisPlayer IsDraw ThisPlayerIDX RoundRobin IsColorChange CardStats

 if {(![uno_ischan $chan])||($UnoMode != 2)||($nick != $ThisPlayer)||($IsColorChange == 1)} {return}

 UnoAutoSkipReset

 if {!$IsDraw} {
  unontc $nick "Ambil kartu dulu dong $nick, baru pass"
  return
 }

 set IsDraw 0
 UnoNextPlayer
 playpass $nick $ThisPlayer
 uno_showcards $ThisPlayer $ThisPlayerIDX
 UnoRobotRestart
 return
}

#
# Color change
#
proc UnoColorChange {nick uhost hand chan txt} {
 global UnoMode IsDraw PlayCard ColorPicker IsColorChange ThisPlayer ThisPlayerIDX RoundRobin

 if {(![uno_ischan $chan])||($UnoMode != 2)||($nick != $ColorPicker)||(!$IsColorChange)} {return}

 UnoAutoSkipReset

 regsub -all \[`.,!{}\ ] $txt "" txt

 set NewColor [string toupper [string range $txt 0 0]]

 switch $NewColor {
  "B" { set PlayCard "B"; set Card " \0030,12 \002B\002lue \003 "}
  "G" { set PlayCard "G"; set Card " \0030,3 \002G\002reen \003 "}
  "Y" { set PlayCard "Y"; set Card " \0031,8 \002Y\002ellow \003 "}
  "R" { set PlayCard "R"; set Card " \0030,4 \002R\002ed \003 "}
  default { unontc $nick "Pilih warna yang akan dimainkan \(r,g,b,y\)"; return }
 }

 UnoNextPlayer

 unomsg "[unonik $ColorPicker] \00314memilih $Card \00314lanjut \00305$ThisPlayer"

 uno_showcards $ThisPlayer $ThisPlayerIDX

 set IsDraw 0
 set IsColorChange 0
 set ColorPicker ""

 UnoRobotRestart

 return
}

#
# Skip card
#
proc UnoPlaySkipCard {nick pickednum crd} {
 global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats

 incr CardStats(played)
 incr CardStats(skips)

 UnoRemoveCardFromHand $nick $pickednum

 set PlayCard $crd
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 set SkipPlayer $ThisPlayer

 UnoNextPlayer

 set SkippedPlayer [lindex $RoundRobin $ThisPlayerIDX]

 UnoNextPlayer

 if {[UnoCheckWin $SkipPlayer $Card]} { return }

 playskip $nick $Card $SkippedPlayer $ThisPlayer

 uno_checkuno $SkipPlayer

 uno_showcards $ThisPlayer $ThisPlayerIDX

 set IsDraw 0
}

#
# Reverse card
#
proc UnoPlayReverseCard {nick pickednum crd} {
 global IsDraw UnoIDX ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats

 incr CardStats(played)
 incr CardStats(skips)

 UnoRemoveCardFromHand $nick $pickednum

 set PlayCard $crd
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 # reverse roundrobin and move to next player
 set NewRoundRobin ""
 set OrigOrderLength [llength $RoundRobin]
 set IDX $OrigOrderLength

 while {$OrigOrderLength != [llength $NewRoundRobin]} {
  set IDX [expr ($IDX - 1)]
  lappend NewRoundRobin [lindex $RoundRobin $IDX]
 }

 set Newindexorder ""
 set OrigindexLength [llength $UnoIDX]
 set IDX $OrigindexLength

 while {$OrigindexLength != [llength $Newindexorder]} {
  set IDX [expr ($IDX - 1)]
  lappend Newindexorder [lindex $UnoIDX $IDX]
 }

 set UnoIDX $Newindexorder
 set RoundRobin $NewRoundRobin

 set ReversePlayer $ThisPlayer

 # next player after reversing roundrobin
 set pcount 0
 while {$pcount != [llength $RoundRobin]} {
  if {[lindex $RoundRobin $pcount] == $ThisPlayer} {
   set ThisPlayerIDX $pcount
   break
  }
  incr pcount
 }

 # less than 3 players acts like a skip card
 if {[llength $RoundRobin] > 2} {
  incr ThisPlayerIDX
  if {$ThisPlayerIDX >= [llength $RoundRobin]} {set ThisPlayerIDX 0}
 }

 set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]

 if {[UnoCheckWin $ReversePlayer $Card]} { return }

 playcard $nick $Card $ThisPlayer

 uno_checkuno $ReversePlayer

 uno_showcards $ThisPlayer $ThisPlayerIDX

 set IsDraw 0
}

#
# Draw Two card
#
proc UnoPlayDrawTwoCard {nick pickednum crd} {
 global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats

 incr CardStats(draws)
 incr CardStats(played)

 UnoRemoveCardFromHand $nick $pickednum

 set PlayCard $crd
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 set DrawPlayer $ThisPlayer
 set DrawPlayerIDX $ThisPlayerIDX

 # move to the player that draws
 UnoNextPlayer

 set PlayerThatDrew $ThisPlayer
 set PlayerThatDrewIDX $ThisPlayerIDX

 # move to the player skipped to
 UnoNextPlayer

 if {[UnoCheckWinDraw $nick $Card $PlayerThatDrew $PlayerThatDrewIDX 2]} { return }

 playdraw $nick $Card $PlayerThatDrew $ThisPlayer

 UnoAddDrawToHand $PlayerThatDrew $PlayerThatDrewIDX 2

 uno_checkuno $nick

 uno_showcards $ThisPlayer $ThisPlayerIDX

 set IsDraw 0
}

#
# Wild Draw Four card
#
proc UnoPlayWildDrawFourCard {nick pickednum crd isrobot} {
 global ThisPlayer ThisPlayerIDX PlayCard RoundRobin IsColorChange ColorPicker CardStats

 incr CardStats(wilds)
 incr CardStats(played)

 set ColorPicker $ThisPlayer

 UnoRemoveCardFromHand $nick $pickednum

 set PlayCard $crd
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 # move to the player that draws
 UnoNextPlayer

 set PlayerThatDrew $ThisPlayer
 set PlayerThatDrewIDX $ThisPlayerIDX

 # bot chooses a color
 if {$isrobot > 0} {
  set cip [UnoBotPickAColor]
  UnoNextPlayer
 }

 if {[UnoCheckWinDraw $nick $Card $PlayerThatDrew $PlayerThatDrewIDX 4]} { return }

 if {$isrobot > 0} {
  botplaywildfour $ColorPicker $PlayerThatDrew $ColorPicker $cip $ThisPlayer
  set ColorPicker ""
  set IsColorChange 0
  uno_showcards $ThisPlayer $ThisPlayerIDX
 } {
  playwildfour $nick $PlayerThatDrew $ColorPicker
  set IsColorChange 1
 }

 UnoAddDrawToHand $PlayerThatDrew $PlayerThatDrewIDX 4

 uno_checkuno $nick

 set IsDraw 0
}

#
# Wild card
#
proc UnoPlayWildCard {nick pickednum crd isrobot} {
 global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin IsColorChange ColorPicker CardStats

 incr CardStats(wilds)
 incr CardStats(played)

 set ColorPicker $ThisPlayer

 UnoRemoveCardFromHand $nick $pickednum

 set PlayCard $crd
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 if {$isrobot > 0} {
  # make a color choice
  set cip [UnoBotPickAColor]
  UnoNextPlayer
 }

 # no cards remaining = winner
 if {[UnoCheckWin $nick $Card]} { return }

 if {$isrobot > 0} {
  botplaywild $nick $ColorPicker $cip $ThisPlayer
  set ColorPicker ""
  uno_showcards $ThisPlayer $ThisPlayerIDX
  set IsColorChange 0
 } {
  playwild $nick $ColorPicker
  set IsColorChange 1
 }

 uno_checkuno $nick

 set IsDraw 0
}

#
# Number card
#
proc UnoPlayNumberCard {nick pickednum crd} {
 global IsDraw ThisPlayer ThisPlayerIDX PlayCard RoundRobin CardStats

 incr CardStats(played)

 UnoRemoveCardFromHand $nick $pickednum

 set PlayCard $crd
 set Card [UnoCardColor $PlayCard]
 UnoAddToDiscardPile $PlayCard

 set NumberCardPlayer $ThisPlayer

 UnoNextPlayer

 if {[UnoCheckWin $NumberCardPlayer $Card]} { return }

 playcard $nick $Card $ThisPlayer

 uno_checkuno $NumberCardPlayer

 uno_showcards $ThisPlayer $ThisPlayerIDX

 set IsDraw 0
}

#
# Player with no cards left wins
#
proc UnoCheckWin {cplayer crd} {
 if {[uno_checkwin $cplayer $crd] > 0} {
  showwin $cplayer $crd
  UnoWin $cplayer
  UnoCycle
  return 1
 }
 return 0
}
proc UnoCheckWinDraw {cplayer crd dplayer dplayeridx num} {
 if {[uno_checkwin $cplayer $crd] > 0} {
  UnoAddDrawToHand $dplayer $dplayeridx $num
  showwin $cplayer $crd
  UnoWin $cplayer
  UnoCycle
  return 1
 }
 return 0
}

#
# Attempt to find card in hand
#
proc UnoFindCard {nick pickednum crd IsRobot} {
 global UnoRobot ThisPlayer ThisPlayerIDX PlayCard UnoWildDrawTwos

  #if {$UnoDebug > 1} {unolog $UnoRobot "UnoFindCard: [lindex $UnoHand($ThisPlayer) $pickednum"}

  set c0 [string range $crd 0 0]
  set c1 [string range $crd 1 1]
  set cip0 [string range $PlayCard 0 0]
  set cip1 [string range $PlayCard 1 1]

  # skip
  if {$c1 == "S"} {
   if {($c0 == $cip0)||($c1 == $cip1)} {
    UnoPlaySkipCard $nick $pickednum $crd
    return 1
   }
   return 0
  }

  # reverse
  if {$c1 == "R"} {
   if {($c0 == $cip0)||($c1 == $cip1)} {
    UnoPlayReverseCard $nick $pickednum $crd
    return 2
   }
   return 0
  }

  # wild draw four
  if {($c0 == "W")&&($c1 == "D")} {
   UnoPlayWildDrawFourCard $nick $pickednum $crd $IsRobot
   return 4
  }

  # wild
  if {$c0 == "W"} {
   UnoPlayWildCard $nick $pickednum $crd $IsRobot
   return 5
  }

  # draw two
  if {$c1 == "D"} {
   set CardOk 0
   if {$c0 == $cip0} {set CardOk 1}
   if {$UnoWildDrawTwos != 0} { 
    if {($cip0 != "W")&&($cip1 == "D")} {set CardOk 1}
    if {$cip1 != ""} {set CardOk 1}
   } {
    if {($cip0 != "W")&&($cip1 == "D")} {set CardOk 1}
   }
   if {$CardOk > 0} {
    UnoPlayDrawTwoCard $nick $pickednum $crd
    return 3
   }
   return 0
  }

  # number card
  if {($c1 == -1)} {return 0}
  if {($c0 == $cip0)||(($cip1 != "")&&($c1 == $cip1))} {
   UnoPlayNumberCard $nick $pickednum $crd
   return 6
  }
  return 0
}

#
# Play a card
#
proc UnoPlayCard {nick uhost hand chan txt} {
 global UnoMode IsDraw IsColorChange ColorPicker UnoPlayers RoundRobin UnoHand ThisPlayer

 if {(![uno_ischan $chan])||($UnoMode != 2)||($nick != $ThisPlayer)||($IsColorChange == 1)} {return}

 UnoAutoSkipReset

 regsub -all \[`,.!{}\ ] $txt "" txt

 if {$txt == ""} {return}

 set pcard [string toupper [string range $txt 0 1]]

 set CardInHand 0

 set pcount 0
 while {[lindex $UnoHand($nick) $pcount] != ""} {
  if {$pcard == [lindex $UnoHand($nick) $pcount]} {
   set pcardnum $pcount
   set CardInHand 1
   break
  }
  incr pcount
 }

 if {!$CardInHand} {
  unontc $nick "Lihat yang bener $nick, ngambil atau mainkan kartu lain yang cocok"
  return
 }

 set CardFound [UnoFindCard $nick $pcardnum $pcard 0]

 switch $CardFound {
  0 {unontc $nick "Oops! Salah dong kartunya... ngambil atau mainkan kartu lain yang cocok"; return}
  4 {return}
  5 {return}
  default {UnoRobotRestart; return}
 }
}

#
# Robot player
#

proc uno_iscolor {c} {
 switch $c {
  "R" { return 1 }
  "G" { return 1 }
  "B" { return 1 }
  "Y" { return 1 }
  default { return 0 }
 }
 return 0
}

proc UnoRobotTryCard {} {
 global PlayCard UnoHand ThisPlayer

 set cip1 [string range $PlayCard 0 0]
 set cip2 [string range $PlayCard 1 1]
 set colorcardinplay [uno_iscolor $cip1]

 set Tier 0
 set TierMax 8

 while {$Tier < $TierMax} {
  set CardCount 0
  while {$CardCount < [llength $UnoHand($ThisPlayer)]} {

   set playcard [lindex $UnoHand($ThisPlayer) $CardCount]

   set hc1 [string range $playcard 0 0]
   set hc2 [string range $playcard 1 1]

    # draw two
    # skip
    # reverse
    # skip or reverse on same color
    # draw four
    # color or number match
    # wild
   set colorcardinhand [uno_iscolor $hc1]

   switch $Tier {
    0 {if {($colorcardinplay)&&($hc1 == $cip1)&&($hc2 == "D")} {return $CardCount}}
    1 {if {($colorcardinplay)&&($cip2 == "D")&&($colorcardinhand)&&($hc2 == "D")} {return $CardCount}}
    2 {if {($cip2 == "S")&&($hc2 == "S")} {return $CardCount}}
    3 {if {($cip2 == "R")&&($hc2 == "R")} {return $CardCount}}
    4 {if {($hc1 == $cip1)&&(($hc2 == "S")||($hc2 == "R"))} {return $CardCount}}
    5 {if {($hc1 == "W")&&($hc2 == "D")} {return $CardCount}}
    6 {if {($hc1 == $cip1)||($hc2 == $cip2)} {return $CardCount}}
    7 {if {($hc1 == "W")} {return $CardCount}}
    default { }
   }
   incr CardCount
  }
  incr Tier
 }
 return -1;
}

proc UnoRobotPlayer {} {
 global UnoIDX IsDraw IsColorChange ColorPicker UnoMode UnoPlayers RoundRobin UnoDeck UnoHand ThisPlayer ThisPlayerIDX PlayCard CardStats UnoRobot

 set CardOk -1
 set IsDraw 0

 set UnoHand($ThisPlayer) [UnoSortHand $UnoHand($ThisPlayer)]

 # look for card in hand
 set CardOk [UnoRobotTryCard]

 # play card if found
 if {$CardOk > -1} {
  set pcard [lindex $UnoHand($ThisPlayer) $CardOk]
  set CardFound [UnoFindCard $UnoRobot $CardOk $pcard 1]
  switch $CardFound {
   0 {unolog $UnoRobot "UnoRobot: oops $pcard"}
   5 {return}
   6 {return}
   default {UnoRobotRestart; return}
  }
 }

 # bot draws a card
 UnoShuffle 1

 set dcardnum [rand [llength $UnoDeck]]
 set dcard [lindex $UnoDeck $dcardnum]
 lappend UnoHand($ThisPlayer) $dcard
 set UnoDeck [lreplace $UnoDeck $dcardnum $dcardnum]

 showwhodrew $UnoRobot

 set UnoHand($ThisPlayer) [UnoSortHand $UnoHand($ThisPlayer)]

 # look for card in hand
 set CardOk [UnoRobotTryCard]

 # bot plays drawn card or passes turn
 if {$CardOk > -1} {
  set pcard [lindex $UnoHand($ThisPlayer) $CardOk]
  set CardFound [UnoFindCard $UnoRobot $CardOk $pcard 1]
  switch $CardFound {
   0 {unolog $UnoRobot "UnoRobot: oops $pcard"}
   5 {return}
   6 {return}
   default {UnoRobotRestart; return}
  }
 } {
  set IsDraw 0
  UnoNextPlayer
  playpass $UnoRobot $ThisPlayer
  uno_showcards $ThisPlayer $ThisPlayerIDX
 }
 return
}

#
# Autoskip inactive players
#
proc UnoAutoSkip {} {
 global UnoMode ThisPlayer ThisPlayerIDX RoundRobin AutoSkipPeriod IsColorChange ColorPicker UnoLogo
 global UnoIDX UnoPlayers UnoDeck UnoHand UnoChan UnoSkipTimer UnoDebug UnoNickColor UnoPaused UnoDCCIDX

 if {($UnoMode != 2)||($UnoPaused != 0)} {return}

 set Idler $ThisPlayer
 set IdlerIDX $ThisPlayerIDX

 if {[uno_isrobot $ThisPlayerIDX]} {unolog "uno" "Autoskip called while robot's turn"; return}

 if {[unotimerexists UnoAutoSkip] != ""} {
  unolog "uno" "Autoskip timer called, but already exists"
  return
 }

 set InChannel 0
 set uclist [chanlist $UnoChan]

 set pcount 0
 while {[lindex $uclist $pcount] != ""} {
  if {[lindex $uclist $pcount] == $Idler} {
   set InChannel 1
   break
  }
  incr pcount
 }

 if {!$InChannel} {
  unomsg "[unonik $Idler] \00314kabur dari channel dan telah dihapus dari daftar pemaen $UnoLogo"
  if {$IsColorChange == 1} {
   if {$Idler == $ColorPicker} {
    # Make A Color Choice
    set cip [UnoPickAColor]
    unomsg "\00303$Idler \00314telah milih warna : kebetulan dia milih $cip"
    set IsColorChange 0
   } {
    unolog "uno" "UnoAutoRemove: Color change set but $Idler not ColorPicker"
   }
  }

  UnoNextPlayer

  unomsg "[unonik $Idler] \00314dilewat, lanjut sekarang giliran [unonik $ThisPlayer]"

  uno_showcards $ThisPlayer $ThisPlayerIDX

  set UnoPlayers [expr ($UnoPlayers -1)]

  # remove player from game and put cards back in deck
  if {$UnoPlayers > 1} {
   set RoundRobin [lreplace $RoundRobin $IdlerIDX $IdlerIDX]
   set UnoIDX [lreplace $UnoIDX $IdlerIDX $IdlerIDX]
   lappend UnoDeck $UnoHand($Idler)
   if [info exist UnoHand($Idler)] {unset UnoHand($Idler)}
   if [info exist UnoNickColor($Idler)] {unset UnoNickColor($Idler)}
   if [info exist UnoDCCIDX($Idler)] {unset UnoDCCIDX($Idler)}
  }

  switch $UnoPlayers {
   1 {
      showwindefault $ThisPlayer
      UnoWin $ThisPlayer
      UnoCycle
     }
   0 {
      unochanmsg "\00314ga ada pemaen, ga ada yang menang... \00303kocok lagi ach"
      UnoCycle
     }
   default {
      if {![uno_isrobot $ThisPlayerIDX]} {
       UnoAutoSkipReset
       UnoRobotRestart
      }
     }
  }
  return
 }

 if {$UnoDebug > 0} {unolog "uno" "AutoSkip Player: $Idler"}

 unomsg "[unonik $Idler] \00314malah bengong \00303$AutoSkipPeriod \00314menit...lewatin saja"

 # player was color picker
 if {$IsColorChange == 1} {
  if {$Idler == $ColorPicker} {
   # Make A Color Choice
   set cip [UnoPickAColor]
   unomsg "[unonik $Idler] \00314telah milih warna : kebetulan dia milih $cip"
   set IsColorChange 0
  } {
   unolog "uno" "UnoRemove: IsColorChange set but $Idler not ColorPicker"
  }
 }

 UnoNextPlayer

 unomsg "[unonik $Idler] \00314dilewat, lanjut sekarang giliran [unonik $ThisPlayer]"

 uno_showcards $ThisPlayer $ThisPlayerIDX

 if {[uno_isrobot $ThisPlayerIDX]} {
  UnoRobotRestart
 }

 UnoAutoSkipReset
 return
}

#
# Pause play
#
proc UnoPause {nick uhost hand chan txt} {
 global UnoChan UnoOn UnoMode UnoOpFlags UnoPaused

 if {![uno_ischan $chan]} {return}
 if {$UnoOn != 1} {return}
 if {$UnoMode != 2} {return}

 if {[isop $nick $chan]==1} {
  if {!$UnoPaused} {
   set UnoPaused 1
   UnoUnbindCmds
   unochanmsg "\00314dipaksa istirahat oleh \00303$nick"
  } {
   set UnoPaused 0
   UnoBindCmds
   UnoAutoSkipReset
   unochanmsg "\00314dilanjutkan oleh \00303$nick"
  }
 }
}

#
# Remove user from play
#
proc UnoRemove {nick uhost hand chan txt} {
 global UnoChan UnoOn UnoMode UnoCycleTime UnoIDX UnoPlayers ThisPlayer ThisPlayerIDX RoundRobin UnoDeck DiscardPile UnoHand IsColorChange ColorPicker UnoNickColor UnoOpFlags UnoDCCIDX
 global UnoLogo
 
 if {(![uno_ischan $chan])||(!$UnoOn)||($UnoMode != 2)} {return}

 regsub -all \[`,.!{}] $txt "" txt

 # allow ops to remove another player
 set UnoOpRemove 0

 if {[string length $txt] > 0} {
  if {[isop $nick $chan]==1} {
   # remover is a player
     set pcount 0
     while {[lindex $RoundRobin $pcount] != ""} {
      if {[lindex $RoundRobin $pcount] == $nick} {
       unochanmsg "\00304!remove pemain lain \00314tidak bisa dilakukan oleh pemain, meskipun kamu \00303OP"
       return
      }
      incr pcount
     }
     if {$UnoPlayers > 2} {
set UnoOpRemove 1
set UnoOpNick $nick
set nick $txt 
     } {
unochanmsg "\00304mau curang yah\00314, kan tinggal berdua yang maen... hehehe, remove diri sendiri aja yach"
return
     }
  } {
   unochanmsg "\00304!remove \00314hanya bisa dilakukan oleh \00303OP"
   return
  }
 }

 # remove player if found - put cards back to bottom of deck
 set pcount 0
 set PlayerFound 0
 while {[lindex $RoundRobin $pcount] != ""} {
  if {[string tolower [lindex $RoundRobin $pcount]] == [string tolower $nick]} {
   set PlayerFound 1
   set FoundIDX $pcount
   set nick [lindex $RoundRobin $pcount]
   break
  }
  incr pcount
 }

 if {!$PlayerFound} {return}

 if {$UnoOpRemove > 0} {
  unomsg "[unonik $nick] \00314dikeluarkan dari $UnoLogo \00314oleh \00303$UnoOpNick"
 } {
  unontc $nick "Kamu dikeluarkan dari $UnoLogo \00314game, habis bengong mulu sich."
  unomsg "[unonik $nick] \00314left $UnoLogo"
 }

 # player was color picker
 if {$IsColorChange == 1} {
  if {$nick == $ColorPicker} {
   # Make A Color Choice
   set cip [UnoPickAColor]
   unomsg "[unonik $nick] \00314pilih warna... Nih yang dipilih: $cip"
   set IsColorChange 0
  } {
   unolog "uno" "UnoRemove: IsColorChange Set but $nick not ColorPicker"
  }
 }

 if {$nick == $ThisPlayer} {
  UnoNextPlayer
  if {$UnoPlayers > 2} {
   unomsg "[unonik $nick] \00314lanjut sekarang giliran [unonik $ThisPlayer]"
  }
  UnoAutoSkipReset
 }

 set UnoPlayers [expr ($UnoPlayers -1)]

 # remove player from game and put cards back in deck

 if {$UnoPlayers > 1} {
  set RoundRobin [lreplace $RoundRobin $FoundIDX $FoundIDX]
  set UnoIDX [lreplace $UnoIDX $FoundIDX $FoundIDX]
  lappend DiscardPile $UnoHand($nick)
  if [info exist UnoHand($nick)] {unset UnoHand($nick)}
  if [info exist UnoNickColor($nick)] {unset UnoNickColor($nick)}
  if [info exist UnoDCCIDX($nick)] {unset UnoDCCIDX($nick)}
 }

 set pcount 0
 while {[lindex $RoundRobin $pcount] != ""} {
  if {[lindex $RoundRobin $pcount] == $ThisPlayer} {
   set ThisPlayerIDX $pcount
   break
  }
  incr pcount
 }

 if {$UnoPlayers == 1} {
  showwindefault $ThisPlayer
  UnoWin $ThisPlayer
  UnoCycle
  return
 }

 UnoRobotRestart

 if {!$UnoPlayers} {
  unochanmsg "\00314ga ada pemaen, ga ada yang menang... \00303kocok lagi ach"
  UnoCycle
 }
 return
}

#
# Move to next player
#
proc UnoNextPlayer {} {
 global ThisPlayer ThisPlayerIDX RoundRobin

 incr ThisPlayerIDX
 if {$ThisPlayerIDX >= [llength $RoundRobin]} {set ThisPlayerIDX 0}
 set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]
}

#
# Set global PlayCard to picked color and return colored card 
#
proc uno_getcolorcard {crd} {
 global PlayCard
 set pcol [string range $crd 0 0]
 switch $pcol {
  "R" {set PlayCard "R"; return "\0030,4 \002R\002ed \003"}
  "G" {set PlayCard "G"; return "\0030,3 \002G\002reen \003"}
  "B" {set PlayCard "B"; return "\0030,12 \002B\002lue \003"}
  "Y" {set PlayCard "Y"; return "\0031,8 \002Y\002ellow \003"}
 }
}

#
# Pick a random color for skipped/removed players
#
proc UnoPickAColor {} {
 set ucolors "R G B Y"
 set pcol [lindex $ucolors [rand [llength $ucolors]]]
 return [uno_getcolorcard $pcol]
}

#
# Robot picks a color by checking hand for 1st color card
# found with matching color, else picks color at random
#
proc UnoBotPickAColor {} {
 global UnoHand ThisPlayer ColorPicker

 set hlen [llength $UnoHand($ColorPicker)]

 # draw two
 set CardCount 0
 while {$CardCount < $hlen} {
  set thiscolor [string range [lindex $UnoHand($ColorPicker) $CardCount] 0 0]
  set thiscard [string range [lindex $UnoHand($ColorPicker) $CardCount] 1 1]
  if {([uno_iscolor $thiscolor])&&($thiscard == "D")} { return [uno_getcolorcard $thiscolor] }
  incr CardCount
 }

 # skip/reverse
 set CardCount 0
 while {$CardCount < $hlen} {
  set thiscolor [string range [lindex $UnoHand($ColorPicker) $CardCount] 0 0]
  set thiscard [string range [lindex $UnoHand($ColorPicker) $CardCount] 1 1]
  if {([uno_iscolor $thiscolor])&&(($thiscard == "S")||($thiscard == "R"))} { return [uno_getcolorcard $thiscolor] }
  incr CardCount
 }

 # number card
 set CardCount 0
 while {$CardCount < $hlen} {
  set thiscolor [string range [lindex $UnoHand($ColorPicker) $CardCount] 0 0]
  if {[uno_iscolor $thiscolor]} { return [uno_getcolorcard $thiscolor] }
  incr CardCount
 }

 # wild or wdf remain, pick color at random
 return [UnoPickAColor]
}

#
# Timers
#

# set robot for next turn
proc UnoRobotRestart {} {
 global UnoMode ThisPlayerIDX RobotRestartPeriod UnoBotTimer
 if {$UnoMode != 2} {return}
 if {![uno_isrobot $ThisPlayerIDX]} {return}
 set UnoBotTimer [utimer $RobotRestartPeriod UnoRobotPlayer]
}

# reset autoskip timer
proc UnoAutoSkipReset {} {
 global AutoSkipPeriod UnoMode UnoSkipTimer
 catch {killtimer $UnoSkipTimer}
 if {$UnoMode == 2} { set UnoSkipTimer [timer $AutoSkipPeriod UnoAutoSkip] }
}

#
#
# Channel Triggers
#
#

# game help
proc UnoCmds {nick uhost hand chan txt} {
 global UnoLogo
 if {![uno_ischan $chan]} {return}
 unogntc $nick "$UnoLogo Commands: !uno !suno !remove \[nick\] !unowon \[nick\] !unocmds"
 unogntc $nick "$UnoLogo Stats: !unotop10 \[games\|wins\|21\] !unotop3last !unostats !unorecords"
 unogntc $nick "$UnoLogo Card Commands: jo=join pl=play dr=draw pa=pass co=color"
 unogntc $nick "$UnoLogo Chan Commands: ca=cards cd=card tu=turn od=order ct=count st=stats ti=time"
 return
}

# game version
proc UnoVersion {nick uhost hand chan txt} {
 global UnoVersion
 unochanmsg "$UnoVersion by ARYO \003"
 return
}

# current player order
proc UnoOrder {nick uhost hand chan txt} {
 global UnoMode UnoPlayers RoundRobin
 if {(![uno_ischan $chan])||($UnoMode < 2)} {return}
 unochanmsg "\00308,06 Urutan Pemain \[$UnoPlayers\] \003 \00300,02 $RoundRobin \003"
 return
}

# game running time
proc UnoTime {nick uhost hand chan txt} {
 global UnoMode UnoLogo
 if {(![uno_ischan $chan])||($UnoMode != 2)} {return}
 unochanmsg "\00308,06 Waktu Game \003 \00300,02 [UnoDuration [uno_gametime]] \003"
 return
}

# show player what cards in hand
proc UnoShowCards {nick uhost hand chan txt} {
 global UnoMode UnoHand ThisPlayerIDX

 if {(![uno_ischan $chan])||($UnoMode != 2)} {return}

 if ![info exist UnoHand($nick)] { return }

 set UnoHand($nick) [UnoSortHand $UnoHand($nick)]

 set Card [UnoCardColorAll $nick]

 if {![uno_isrobot $ThisPlayerIDX]} { unontc $nick "$Card\003" }

 return
}

# show current player
proc UnoTurn {nick uhost hand chan txt} {
 global UnoMode ThisPlayer RoundRobin UnoMode
 if {(![uno_ischan $chan])||($UnoMode != 2)} {return}
 if {[llength $RoundRobin] < 1 } {return}
 unochanmsg "\00308,06 Sekarang Giliran \00300,02 $ThisPlayer \003"
 return
}

# show current top card
proc UnoTopCard {nick uhost hand chan txt} {
 global PlayCard UnoMode
 if {(![uno_ischan $chan])||($UnoMode != 2)} {return}
 set pcard $PlayCard
 set Card [UnoCardColor $pcard]
 unochanmsg "$Card"
 return
}

# card stats
proc UnoCardStats {nick uhost hand chan txt} {
 global UnoMode CardStats
 if {(![uno_ischan $chan])||($UnoMode != 2)} {return}
 unochanmsg "\00308,06 Played:$CardStats(played)  Skip\/Rev: $CardStats(skips) WildCards:$CardStats(wilds) \003"
 return
}

# card count
proc UnoCardCount {nick uhost hand chan txt} {
 global RoundRobin UnoHand UnoMode
 if {(![uno_ischan $chan])||($UnoMode != 2)} {return}
 set ordcnt 0
 set crdcnt ""
 while {[lindex $RoundRobin $ordcnt] != ""} {
  set cp [lindex $RoundRobin $ordcnt]
  set cc [llength $UnoHand($cp)]
  append crdcnt "\00308,06 $cp \00300,02 $cc cards "
  incr ordcnt
 }
 unomsg "$crdcnt\003"
 return
}

# player's score
proc UnoWon {nick uhost hand chan txt} {
 global UnoScoreFile UnoPointsName

 if {![uno_ischan $chan]} {return}

 regsub -all \[`,.!] $txt "" txt

 if {![string length $txt]} {set txt $nick}

 set scorer [string tolower $txt]

 set pflag 0

 set f [open $UnoScoreFile r]
 while {[gets $f sc] != -1} {
  set cnick [string tolower [lindex [split $sc] 0]]
  if {$cnick == $scorer} {
   set winratio [format "%4.1f" [expr [lindex $sc 2] /[lindex $sc 1]]]
   set pmsg "\00308,06 [lindex [split $sc] 0] \00300,02 [lindex $sc 2] $UnoPointsName in [lindex $sc 1] Games \($winratio p\/g\) "
   set pflag 1
  }
 }
 close $f

 if {!$pflag} {
  set pmsg "\00308,06 $txt \00300,02 kosong \003"
 }
 unochanmsg "$pmsg"
 return
}

# current top10 list
proc UnoTopTen {nick uhost hand chan txt} {
 if {![uno_ischan $chan]} {return}
 regsub -all \[`,.!{}\ ] $txt "" txt
 set txt [string tolower [string range $txt 0 10]]
 switch $txt {
  "won" {set mode 1}
  "games" {set mode 0}
  "points" {set mode 1}
  "21" {set mode 2}
  "blackjack" {set mode 2}
  default {set mode 1}
 }
 UnoTop10 $mode
 return
}

# last month's top3
proc UnoTopThreeLast {nick uhost hand chan txt} {
 if {![uno_ischan $chan]} {return}
 UnoLastMonthTop3 $nick $uhost $hand $chan 0
 UnoLastMonthTop3 $nick $uhost $hand $chan 1
 return
}

# month's stats
proc UnoPlayStats {nick uhost hand chan txt} {
 global UnoFast UnoHigh UnoPlayed UnoPointsName
 if {![uno_ischan $chan]} {return}
 set msg "\00308,06 Fast \00300,02 [lindex [split $UnoFast] 0] [UnoDuration [lindex $UnoFast 1]] "
 append msg "\00308,06 High \00300,02 [lindex [split $UnoHigh] 0] [lindex $UnoHigh 1] $UnoPointsName "
 append msg "\00308,06 Played \00300,02 [lindex [split $UnoPlayed] 0] [lindex $UnoPlayed 1] Cards \003"
 unochanmsg "$msg"
 return
}

# all-time records
proc UnoRecords {nick uhost hand chan txt} {
 global UnoRecordFast UnoRecordHigh UnoRecordCard UnoRecordWins UnoRecordPlayed
 if {![uno_ischan $chan]} {return}
 unochanmsg "\00308,06 Points \00300,02 $UnoRecordCard \00308,06 Games \00300,02 $UnoRecordWins \00308,06 Fast \00300,02 [lindex $UnoRecordFast 0] [UnoDuration [lindex $UnoRecordFast 1]] \00308,06 High Score \00300,02 $UnoRecordHigh \00308,06 Cards Played \00300,02 $UnoRecordPlayed \003"
 return
}

# current row (streak)
proc UnoCurrentRow {nick uhost hand chan txt} {
 global UnoLastWinner UnoWinsInARow
 if {![uno_ischan $chan]} {return}
 if {($UnoLastWinner != "")&&($UnoWinsInARow > 0)} {
  switch ($UnoWinsInARow) {
   1 { unochanmsg "\00303$UnoLastWinner \00314has won \00304$UnoWinsInARow game" }
   default { unochanmsg "\00303$UnoLastWinner \00314is on a \00304$UnoWinsInARow game streak" }
  }
 }
 return
}

# month top10
proc UnoTop10 {mode} {
 global UnoScoreFile unsortedscores UnoPointsName UnoRobot

 if {($mode < 0)||($mode > 2)} {set mode 0}

 switch $mode {
  0 {set winners "\00314Top 10 \00303Paling Banyak Menang "}
  1 {set winners "\00314Top 10 \00303Penggemar $UnoPointsName "}
  2 {set winners "\00314Top 10 \00303Blackjacks "}
 }

 if ![file exists $UnoScoreFile] {
  set f [open $UnoScoreFile w]
  puts $f "$UnoRobot 0 0 0"
  unochanmsg "\00314scores \00303direset"
  close $f
  return
 } {
  unomsg "\002$winners\002"
  set winners ""
 }

 if [info exists unsortedscores] {unset unsortedscores}
 if [info exists top10] {unset top10}

 set f [open $UnoScoreFile r]
 while {[gets $f s] != -1} {
  switch $mode {
   0 {set unsortedscores([lindex [split $s] 0]) [lindex $s 1]}
   1 {set unsortedscores([lindex [split $s] 0]) [lindex $s 2]}
   2 {set unsortedscores([lindex [split $s] 0]) [lindex $s 3]}
  }
 }
 close $f

 for {set s 0} {$s < 10} {incr s} {
  set top10($s) "Nobody 0"
 }

 set s 0
 foreach n [lsort -decreasing -command UnoSortScores [array names unsortedscores]] {
  set top10($s) "$n $unsortedscores($n)"
  incr s
 }

 for {set s 0} {$s < 10} {incr s} {
  if {[llength [lindex $top10($s) 0]] > 0} {
   if {[lindex [split $top10($s)] 0] != "Nobody"} {
    append winners "\00308,06 #[expr $s +1] \00300,02 [lindex [split $top10($s)] 0] [lindex $top10($s) 1] "
   }
  }
 }

 unomsg "$winners \003"
 return
}

#
# Last month's top3
#
proc UnoLastMonthTop3 {nick uhost hand chan txt} {
 global UnoLastMonthCards UnoLastMonthGames UnoPointsName
 if {![uno_ischan $chan]} {return}
 if {!$txt} {
  if [info exists UnoLastMonthCards] {
   set UnoTop3 ""
   unochanmsg "\002\00314Last Month's Top 3 \00303$UnoPointsName \00314Fans\002"
   for { set s 0} { $s < 3 } { incr s} {
    append UnoTop3 "\00308,06 #[expr $s +1] \00300,02 $UnoLastMonthCards($s) "
   }
   unomsg "$UnoTop3"
  }
 } {
  if [info exists UnoLastMonthGames] {
   set UnoTop3 ""
   unochanmsg "\002\00314Last Month's Top 3 \00303Game Winners\002"
   for { set s 0} { $s < 3 } { incr s} {
    append UnoTop3 "\00308,06 #[expr $s +1] \00300,02 $UnoLastMonthGames($s) "
   }
   unomsg "$UnoTop3"
  }
 }
}

#
# Read score file
#
proc UnoReadScores {} {
 global unogameswon unoptswon unoblackjackswon UnoScoreFile UnoRobot

 if [info exists unogameswon] { unset unogameswon }
 if [info exists unoptswon] { unset unoptswon }
 if [info exists unoblackjackswon] { unset unoblackjackswon }

 if ![file exists $UnoScoreFile] {
  set f [open $UnoScoreFile w]
  puts $f "$UnoRobot 0 0 0"
  close $f
 }

 set f [open $UnoScoreFile r]
 while {[gets $f s] != -1} {
  set unogameswon([lindex [split $s] 0]) [lindex $s 1]
  set unoptswon([lindex [split $s] 0]) [lindex $s 2]
  set unoblackjackswon([lindex [split $s] 0]) [lindex $s 3]
 }
 close $f

 return
}

#
# Clear top10 and write monthly scores
#
proc UnoNewMonth {min hour day month year} {
 global unsortedscores unogameswon unoptswon unoblackjackswon UnoLastMonthCards UnoLastMonthGames UnoScoreFile UnoRobot
 global UnoFast UnoHigh UnoPlayed UnoRecordFast UnoRecordHigh UnoRecordPlayed UnoRecordCard UnoRecordWins

 set lmonth [UnoLastMonthName $month]

 unochanmsg "\002\00304Clearing monthly scores\002"

 set UnoMonthFileName "$UnoScoreFile.$lmonth"

 # read current scores
 UnoReadScores

 # write to old month file
 if ![file exists $UnoMonthFileName] {
  set f [open $UnoMonthFileName w]
  foreach n [array names unogameswon] {
   puts $f "$n $unogameswon($n) $unoptswon($n) $unoblackjackswon($n)"
  }
  close $f
 }

 # find top 3 card holders and game winners
 set mode 0 

 while {$mode < 2} {
  if [info exists unsortedscores] {unset unsortedscores}
  if [info exists top10] {unset top10}

  set f [open $UnoScoreFile r]
  while {[gets $f s] != -1} {
   switch $mode {
    0 {set unsortedscores([lindex [split $s] 0]) [lindex $s 1]}
    1 {set unsortedscores([lindex [split $s] 0]) [lindex $s 2]}
   }
  }
  close $f

  set s 0
  foreach n [lsort -decreasing -command UnoSortScores [array names unsortedscores]] {
   set top10($s) "$n $unsortedscores($n)"
   incr s
  }

  for {set s 0} {$s < 3} {incr s} {
   if {[lindex $top10($s) 1] > 0} {
    switch $mode {
     0 {set UnoLastMonthGames($s) "[lindex [split $top10($s)] 0] [lindex $top10($s) 1]"}
     1 {set UnoLastMonthCards($s) "[lindex [split $top10($s)] 0] [lindex $top10($s) 1]"}
    }
   } {
    switch $mode {
     0 {set UnoLastMonthGames($s) "Nobody 0"}
     1 {set UnoLastMonthCards($s) "Nobody 0"}
    }
   }
  }
  incr mode
 }

 # update records
 if {[lindex $UnoFast 1] < [lindex $UnoRecordFast 1]} {set UnoRecordFast $UnoFast}
 if {[lindex $UnoHigh 1] > [lindex $UnoRecordHigh 1]} {set UnoRecordHigh $UnoHigh}
 if {[lindex $UnoPlayed 1] > [lindex $UnoRecordPlayed 1]} {set UnoRecordPlayed $UnoPlayed}
 if {[lindex $UnoLastMonthCards(0) 1] > [lindex $UnoRecordCard 1]} {set UnoRecordCard $UnoLastMonthCards(0)}
 if {[lindex $UnoLastMonthGames(0) 1] > [lindex $UnoRecordWins 1]} {set UnoRecordWins $UnoLastMonthGames(0)}

 # wipe last months records
 set UnoFast "$UnoRobot 60"
 set UnoHigh "$UnoRobot 100"
 set UnoPlayed "$UnoRobot 100"

 # save top3 and records to config file
 UnoWriteCFG

 # wipe this months score file
 set f [open $UnoScoreFile w]
 puts $f "$UnoRobot 0 0 0"
 close $f

 unolog "uno" "cleared monthly scores"
 return
}

#
# Update score of winning player
#
proc UnoUpdateScore {winner cardtotals blackjack} {
 global unogameswon unoptswon unoblackjackswon UnoScoreFile

 UnoReadScores

 if {[info exists unogameswon($winner)]} {
  incr unogameswon($winner)
 } {
  set unogameswon($winner) 1
 }

 if {[info exists unoptswon($winner)]} {
  incr unoptswon($winner) $cardtotals
 } {
  set unoptswon($winner) $cardtotals
 }

 if {$blackjack > 0} {
  if {[info exists unoblackjackswon($winner)]} {
   incr unoblackjackswon($winner)
  } {
   set unoblackjackswon($winner) 1
  }
 } {
  if {![info exists unoblackjackswon($winner)]} {
   set unoblackjackswon($winner) 0
  }
 }

 set f [open $UnoScoreFile w]
 foreach n [array names unogameswon] {
  puts $f "$n $unogameswon($n) $unoptswon($n) $unoblackjackswon($n)"
 }
 close $f

 return
}

#
# Display winner and game statistics
#
proc UnoWin {winner} {
 global UnoHand ThisPlayer RoundRobin UnoPointsName CardStats UnoMode UnoCycleTime botnick
 global UnoFast UnoHigh UnoPlayed UnoBonus UnoWinDefault UnoDCCIDX UnoRobot UnoLastWinner UnoWinsInARow
 
 # get time game finished
 set UnoTime [uno_gametime]

 set cardtotals 0
 set UnoMode 3
 set ThisPlayerIDX 0
 set needCFGWrite 0
 set isblackjack 0
 set cardtake 0

 # colour winner's nick
 set cnick [unonik $winner]

 #unomsg "\0030,6 Card Totals \003"

 # total up all player's cards
 while {$ThisPlayerIDX != [llength $RoundRobin]} {
  set Card ""
  set ThisPlayer [lindex $RoundRobin $ThisPlayerIDX]
  if [info exist UnoDCCIDX($ThisPlayer)] {unset UnoDCCIDX($ThisPlayer)}
  if {$ThisPlayer != $winner} {
   set ccount 0
   while {[lindex $UnoHand($ThisPlayer) $ccount] != ""} {
    set cardtotal [lindex $UnoHand($ThisPlayer) $ccount]
    set c1 [string range $cardtotal 0 0]
    set c2 [string range $cardtotal 1 1]
    set cardtotal 0

    if {$c1 == "W"} {
     set cardtotal 50
    } {
     switch $c2 {
      "S" {set cardtotal 20}
      "R" {set cardtotal 20}
      "D" {set cardtotal 20}
      default {set cardtotal $c2}
     }
    }
    set cardtotals [expr $cardtotals + $cardtotal]
    incr ccount
   }
   set Card [UnoCardColorAll $ThisPlayer]
   unochanmsg "\00305$ThisPlayer \00303\[$ccount\] $Card"
   incr cardtake $ccount
  }
  incr ThisPlayerIDX
 }

 set bonus 0
 # add up possibe bonuses
 if {$UnoWinDefault != 1} {
  set HighScore [lindex $UnoHigh 1]
  set HighPlayed [lindex $UnoPlayed 1]
  set FastRecord [lindex $UnoFast 1]

  # out with 21 adds blackjack bonus
  if {$cardtotals == 21} {
   set bbonus [expr $UnoBonus /2]
   unochanmsg "\00305$cnick \00314goes out on 21! \0030,4 \002$bbonus\002 Blackjack Bonus $UnoPointsName \003"
   incr bonus $bbonus
   set isblackjack 1
  }
  # high score record
  if {$cardtotals > $HighScore} {
   unochanmsg "\00305$cnick \00314broke the \00303High Score Record \0030,4 $UnoBonus bonus $UnoPointsName \003"
   set UnoHigh "$winner $cardtotals"
   incr bonus $UnoBonus
  }
  # played cards record
  if {$CardStats(played) > $HighPlayed} {
   unochanmsg "\00305$cnick \00314broke the \00303Most Cards Played Record \0030,4 $UnoBonus bonus $UnoPointsName \003"
   set UnoPlayed "$winner $CardStats(played)"
   incr bonus $UnoBonus
  }
  # fast game record
  if {($UnoTime < $FastRecord)&&($winner != $UnoRobot)} {
   unochanmsg "\00305$cnick \00314broke the \00303Fast Game Record \0030,4 $UnoBonus bonus $UnoPointsName \003"
   incr bonus $UnoBonus
   set UnoFast "$winner $UnoTime"
  }
 }

 # win streak bonus
 if {$winner == $UnoLastWinner} {
  incr UnoWinsInARow
  set RowMod [expr {$UnoWinsInARow %3}]
  if {$RowMod == 0} {
   set RowBonus [expr int((pow(2,($UnoWinsInARow/3)-1)*($UnoBonus/4)))]
   unochanmsg "\00305$cnick \00314 menang \00303$UnoWinsInARow \00314kali berturut-turut dan dapet \00303$RowBonus \00314bonus \00303$UnoPointsName \003"
   incr bonus $RowBonus
  }
 } {
  if {($UnoLastWinner != "")&&($UnoWinsInARow > 1)} {
   unochanmsg "\00305$cnick \00314berhasil menjegal \00303$UnoWinsInARow \00314kali kemenangan \00305$UnoLastWinner \003"
  }
  set UnoLastWinner $winner
  set UnoWinsInARow 1
 }

 # show winner
 set msg "\00305$cnick \00314mendapatkan \002\00303$cardtotals \00304$UnoPointsName\002 \00314karena mendapat \00303$cardtake \00303kartu \003"

 # add bonus
 if {$bonus > 0} {
  incr cardtotals $bonus
  set needCFGWrite 1
  append msg "\00314Total: \002\00303$cardtotals \00304$UnoPointsName \002\003"
 }

 unochanmsg "$msg"

 # show game stats
 unochanmsg "\00303$CardStats(played) \00303kartu \00314dimainkan dalam waktu \00310[UnoDuration $UnoTime]\003"

 # write scores
 UnoUpdateScore $winner $cardtotals $isblackjack

 # write records
 if {$needCFGWrite > 0} {UnoWriteCFG}

 return
}

#
# Re-shuffle deck
#
proc UnoShuffle {cardsneeded} {
 global UnoDeck DiscardPile

 # no need in shuffling if more cards remain than needed
 if {[llength $UnoDeck] >= $cardsneeded} { return }

 unochanmsg "\00304Re-Shuffling Deck \003"

 set DeckLeft 0
 while {$DeckLeft < [llength $UnoDeck]} {
  lappend DiscardPile [lindex $UnoDeck $DeckLeft]
  incr DeckLeft
 }

 set UnoDeck ""
 set NewDeckSize [llength $DiscardPile]
 while {[llength $UnoDeck] != $NewDeckSize} {
  set pcardnum [rand [llength $DiscardPile]]
  set pcard [lindex $DiscardPile $pcardnum]
  lappend UnoDeck $pcard
  set DiscardPile [lreplace $DiscardPile $pcardnum $pcardnum]
 }
 return
}

#
# Read config file
#
proc UnoReadCFG {} {
 global UnoChan UnoCFGFile UnoLastMonthCards UnoLastMonthGames UnoPointsName UnoScoreFile UnoStopAfter UnoBonus
 global UnoFast UnoHigh UnoPlayed UnoRecordHigh UnoRecordFast UnoRecordCard UnoRecordWins UnoRecordPlayed UnoWildDrawTwos UnoAds

 if {[file exist $UnoCFGFile]} {
  set f [open $UnoCFGFile r]
  while {[gets $f s] != -1} {
   set kkey [string tolower [lindex [split $s "="] 0]]
   set kval [lindex [split $s "="] 1]
   switch $kkey {
    channel {set UnoChan $kval}
    points {set UnoPointsName $kval}
    scorefile {set UnoScoreFile $kval}
    stopafter {set UnoStopAfter $kval}
    wilddrawtwos {set UnoWildDrawTwos $kval}
    lastmonthcard1 {set UnoLastMonthCards(0) $kval}
    lastmonthcard2 {set UnoLastMonthCards(1) $kval}
    lastmonthcard3 {set UnoLastMonthCards(2) $kval}
    lastmonthwins1 {set UnoLastMonthGames(0) $kval}
    lastmonthwins2 {set UnoLastMonthGames(1) $kval}
    lastmonthwins3 {set UnoLastMonthGames(2) $kval}
    ads {set UnoAds $kval}
    fast {set UnoFast $kval}
    high {set UnoHigh $kval}
    played {set UnoPlayed $kval}
    bonus {set UnoBonus $kval}
    recordhigh {set UnoRecordHigh $kval}
    recordfast {set UnoRecordFast $kval}
    recordcard {set UnoRecordCard $kval}
    recordwins {set UnoRecordWins $kval}
    recordplayed {set UnoRecordPlayed $kval}
   }
  }
  close $f
  if {$UnoStopAfter < 0} {set UnoStopAfter 0}
  if {$UnoBonus <= 0} {set UnoBonus 100}
  if {($UnoWildDrawTwos < 0)||($UnoWildDrawTwos > 1)} {set UnoWildDrawTwos 0}
  if {($UnoAds < 0)||($UnoAds > 1)} {set UnoAds 1}
  return
 }
 putcmdlog "\[Uno\] config file $UnoCFGFile not found... saving defaults"
 UnoWriteCFG
 return
}

#
# Write config file
#
proc UnoWriteCFG {} {
 global UnoChan UnoCFGFile UnoLastMonthCards UnoLastMonthGames UnoPointsName UnoScoreFile UnoStopAfter UnoBonus
 global UnoFast UnoHigh UnoPlayed UnoRecordHigh UnoRecordFast UnoRecordCard UnoRecordWins UnoRecordPlayed UnoWildDrawTwos UnoAds

 set f [open $UnoCFGFile w]

 puts $f "# This file is automatically overwritten"
 puts $f "Channel=$UnoChan"
 puts $f "Points=$UnoPointsName"
 puts $f "ScoreFile=$UnoScoreFile"
 puts $f "StopAfter=$UnoStopAfter"
 puts $f "WildDrawTwos=$UnoWildDrawTwos"
 puts $f "Ads=$UnoAds"
 puts $f "LastMonthCard1=$UnoLastMonthCards(0)"
 puts $f "LastMonthCard2=$UnoLastMonthCards(1)"
 puts $f "LastMonthCard3=$UnoLastMonthCards(2)"
 puts $f "LastMonthWins1=$UnoLastMonthGames(0)"
 puts $f "LastMonthWins2=$UnoLastMonthGames(1)"
 puts $f "LastMonthWins3=$UnoLastMonthGames(2)"
 puts $f "Fast=$UnoFast"
 puts $f "High=$UnoHigh"
 puts $f "Played=$UnoPlayed"
 puts $f "Bonus=$UnoBonus"
 puts $f "RecordHigh=$UnoRecordHigh"
 puts $f "RecordFast=$UnoRecordFast"
 puts $f "RecordCard=$UnoRecordCard"
 puts $f "RecordWins=$UnoRecordWins"
 puts $f "RecordPlayed=$UnoRecordPlayed"

 close $f
 return
}

#
# Score advertiser
#
proc UnoScoreAdvertise {} {
 global UnoChan UnoAdNumber UnoRobot

 switch $UnoAdNumber {
  0 {UnoTop10 1}
  1 {UnoLastMonthTop3 $UnoRobot none none $UnoChan 0}
  2 {UnoTop10 0}
  3 {UnoRecords $UnoRobot none none $UnoChan ""}
  4 {UnoTop10 2}
  5 {UnoPlayed $UnoRobot none none $UnoChan ""}
  6 {UnoHighScore $UnoRobot none none $UnoChan ""}
  7 {UnoTopFast $UnoRobot none none $UnoChan ""}
 }

 incr UnoAdNumber

 if {$UnoAdNumber > 7} {set UnoAdNumber 0}

 return
}

#
# Sort cards in hand
#
proc UnoSortHand {playerhand} {
 set uhand [lsort -dictionary $playerhand]
 return $uhand
}

#
# Color all cards in hand
#
proc UnoCardColorAll {cplayer} {
 global UnoHand
 set ccard ""
 set ccount 0
 set hcount [llength $UnoHand($cplayer)]
 while {$ccount != $hcount} {
  append ccard [UnoCardColor [lindex $UnoHand($cplayer) $ccount]]
  incr ccount
 }
 return $ccard
}

#
# Color a single card
#
proc UnoCardColor {pcard} {
 global UnoRedCard UnoGreenCard UnoBlueCard UnoYellowCard UnoSkipCard UnoReverseCard UnoDrawTwoCard
 global UnoWildCard UnoWildDrawFourCard
  set cCard ""
  set c1 [string range $pcard 1 1]
  switch [string range $pcard 0 0] {
   "W" {
     if {$c1 == "D"} {
      append cCard $UnoWildDrawFourCard
     } {
      append cCard $UnoWildCard
     }
     return $cCard
    }
   "R" {append cCard $UnoRedCard}
   "G" {append cCard $UnoGreenCard}
   "B" {append cCard $UnoBlueCard}
   "Y" {append cCard $UnoYellowCard}
  }
  switch $c1 {
   "S" {append cCard $UnoSkipCard}
   "R" {append cCard $UnoReverseCard}
   "D" {append cCard $UnoDrawTwoCard}
   default {append cCard "$c1 \003 "}
  }
  return $cCard
}

#
# Check if player has Uno
#
proc uno_checkuno {cplayer} {
 global UnoHand
 if {[llength $UnoHand($cplayer)] > 1} {return}
 uno_hasuno $cplayer
 return
}

#
# Check for winner
#
proc uno_checkwin {cplayer ccard} {
 global UnoHand
 if {[llength $UnoHand($cplayer)] > 0} {return 0}
 return 1
}

#
# Show player what cards they have
#
proc uno_showcards {cplayer cplayeridx} {
 global UnoIDX
 if {[uno_isrobot $cplayeridx]} {return}
 set cds [UnoCardColorAll $cplayer]
 unontc [lindex $UnoIDX $cplayeridx] "Kartumu: $cds"
}

#
# Check if this is the robot player
#
proc uno_isrobot {cplayerIDX} {
 global RoundRobin UnoRobot UnoMaxNickLen
 if {[string range [lindex $RoundRobin $cplayerIDX] 0 $UnoMaxNickLen] != $UnoRobot} {return 0}
 return 1
}

# Show played card
proc playcard {who crd nplayer} {
 unomsg "\00305$who \00314mainkan $crd \00314ke \00305$nplayer"
}

# Show played draw card
proc playdraw {who crd dplayer nplayer} {
 global UGood
 unomsg "\00304[lindex $UGood [rand [llength $UGood]]] \00305$who \00314mainkan $crd \00314... \00305$dplayer \00314sabar dolo ya! ambil tuh\00303 2 kartu \00304Gluk, Gluk!\00314, lewat... lanjut \00305$nplayer"
}

# Show played wildcard
proc playwild {who chooser} {
 global UnoWildCard
 unomsg "\00305$who \00314mainkan $UnoWildCard \00314pilih \00303warna \00305$chooser"
}

# Show played wild draw four
proc playwildfour {who skipper chooser} {
 global UnoWildDrawFourCard UGood
 unomsg "\00304[lindex $UGood [rand [llength $UGood]]] \00305$who \00314mainkan $UnoWildDrawFourCard \00314lagi-lagi \00305$skipper \00314bengong dan terpaksa ambil\00303 4 kartu \00304Glek, Glek, Glek, Glek!\00314, lewat... "
 unomsg "\00314Hmm... kayaknya \00305$who \00314sengaja bikin buncit \00305$skipper\00314, ya kan? Ayo pilih \00303warna \00305$chooser"
}

# Show played skip card
proc playskip {who crd skipper nplayer} {
 global UGood
 unomsg "\00304[lindex $UGood [rand [llength $UGood]]] \00305$who \00314mainkan $crd \00314dan berhasil bikin bengong \00305$skipper,\00314 lewat... lanjut \00305$nplayer"
}

proc showwhodrew {who} {
 global UDraw
 unomsg "\00305$who \00314terpaksa ngambil \00303kartu... \00304[lindex $UDraw [rand [llength $UDraw]]]\003"
}

proc playpass {who nplayer} {
 global UBad
 unomsg "\00305$who \00314pass ke \00305$nplayer\00314... \00304[lindex $UBad [rand [llength $UBad]]]"
}

proc botplaywild {who chooser ncolr nplayer} {
 global UnoWildCard
 unomsg "\00305$who \00314mainkan $UnoWildCard \00314dan memilih \00303$ncolr \00314lanjut \00305$nplayer"
}

# Show played wild draw four
proc botplaywildfour {who skipper chooser choice nplayer} {
 global UnoWildDrawFourCard
 unomsg "\00305$who \00314mainkan $UnoWildDrawFourCard\00314, terpaksa \00305$skipper \00314ambil\00303 4 kartu \00304Glek, Glek, Glek, Glek!\00314, lewat... \00305$chooser pilih $choice \00314lanjut \00305$nplayer"
}

# Show a player what they drew
proc showdraw {idx crd} {
 global UnoIDX
 if {[uno_isrobot $idx]} {return}
 unontc [lindex $UnoIDX $idx] "Kartu yang ke ambil: $crd"
}

# Show Win 
proc showwin {who crd} {
 global UnoLogo 
 unomsg "\00305$who \00314mainkan $crd \00314dan horeee akhirnya.... berhasl memenangkan $UnoLogo \00314dan \002\00304Bunga!\002"
}

# Show Win by default
proc showwindefault {who} {
 global UnoWinDefault UnoLogo
 unomsg "\00305$who \002\00303W\00312i\00313n\00305s\002 $UnoLogo \00314... gak ada lawan sich..."
 set UnoWinDefault 1
}

# Player Has Uno
proc uno_hasuno {who} {
 global UnoChan UnoLogo
 putquick "PRIVMSG $UnoChan :\001ACTION \00314cuman ingetin... \00305$who \00304sudah \002\0033U\00312N\00313O\00305!\002\001"
}

#
# Utility functions
#

# Check if a timer exists
proc unotimerexists {cmd} {
 foreach i [timers] {
  if {![string compare $cmd [lindex $i 1]]} then {
   return [lindex $i 2]
  }
 }
 return
}

# Sort scores
proc UnoSortScores {s1 s2} {
 global unsortedscores
 if {$unsortedscores($s1) <  $unsortedscores($s2)} {return -1}
 if {$unsortedscores($s1) == $unsortedscores($s2)} {return 0}
 if {$unsortedscores($s1) >  $unsortedscores($s2)} {return 1}
}

# Calculate game running time
proc uno_gametime {} {
 global UnoStartTime
 set UnoCurrentTime [unixtime]
 set gt [expr ($UnoCurrentTime - $UnoStartTime)]
 return $gt
}

# Colorize nickname
proc unonik {nick} {
 global UnoNickColor
 return "\00305$nick"
}
proc unocolornick {pnum} {
 global UnoNickColors
 set c [lindex $UnoNickColors [expr $pnum-1]]
 set nik [format "%02d" $c]
 return $nik
}

# Ratio of two numbers
proc unoget_ratio {num den} {
 set n 0.0
 set d 0.0
 set n [expr $n +$num]
 set d [expr $d +$den]
 if {!$d} {return 0}
 set ratio [expr (($n /$d) *100.0)]
 return $ratio
}

# Name of last month
proc UnoLastMonthName {month} {
 switch $month {
  00 {return "Dec"}
  01 {return "Jan"}
  02 {return "Feb"}
  03 {return "Mar"}
  04 {return "Apr"}
  05 {return "May"}
  06 {return "Jun"}
  07 {return "Jul"}
  08 {return "Aug"}
  09 {return "Sep"}
  10 {return "Oct"}
  11 {return "Nov"}
  default {return "???"}
 }
}

# Pad a string with spaces
proc unostrpad {str len} {
 set slen [string length $str]
 if {$slen > $len} {return $str}
 while {$slen < $len} {
  append str " "
  incr slen
 }
 return $str
}

#
# Get time interval in min and sec
#
proc UnoDuration {sec} {
  set s ""
  if {$sec >= 3600} {
   set tmp [expr $sec / 3600]
   set s [format "%s\00303%d\003 \00303jam \003" $s $tmp]
   set sec [expr $sec - ($tmp*3600)]
  }
  if {$sec >= 60} {
   set tmp [expr $sec / 60]
   set s [format "%s\00303%d\003 \00303menit \003" $s $tmp]
   set sec [expr $sec - ($tmp*60)]
  }
  if {$sec > 0} {
   set tmp $sec
   set s [format "%s\00303%d\003 \00303detik\003" $s $tmp]
  }
  return $s
}

#
# Channel and DCC messages
#

proc unomsg {what} {
 global UnoChan
 putquick "PRIVMSG $UnoChan :$what"
}

proc unochanmsg {what} {
 global UnoChan UnoLogo
 putquick "PRIVMSG $UnoChan :$UnoLogo $what"
}

proc unogntc {who what} {
 global UnoNTC
 putquick "$UnoNTC $who :$what"
}

proc unontc {who what} {
 global UnoNTC UnoDCCIDX
 if {$UnoDCCIDX($who) != -1} {
  putdcc $UnoDCCIDX($who) $what
 } {
  putquick "$UnoNTC $who :$what"
 }
}

proc unolog {who what} {
 putcmdlog "\[$who\] $what"
}

#
# DCC routines
#

proc unologin:dcc {hand idx} {
  global UnoChan UnoOn UnoDCCIDX RoundRobin

  if ![handonchan $hand $UnoChan] {return 0}

  set tnick [hand2nick $hand $UnoChan]
  if {($tnick == "")||($tnick == "*")} {return 0}
  if ![info exist UnoDCCIDX($tnick)] {return 0}

  set pcount 0
  while {[lindex $RoundRobin $pcount] != ""} {
   set pnick [lindex $RoundRobin $pcount]
   if {$pnick == $tnick} {
    if {[info exist UnoDCCIDX($pnick)]} {
     set UnoDCCIDX($pnick) $idx
     unolog "Uno" "$pnick on new dcc socket $idx"
     break
    }
   }
   incr pcount
  }
  return 0
}

proc unologout:dcc {hand idx} {
  global UnoChan UnoDCCIDX party-chan party-just-quit
  if {[info exists party-just-quit] && ${party-just-quit} == $hand} {unset party-just-quit ; return 0}
  if ![handonchan $hand $UnoChan] {return 0}
  set tnick [hand2nick $hand $UnoChan]
  if {($tnick == "")||($tnick == "*")} {return 0}
  if {[info exist UnoDCCIDX($tnick)]} {
   unolog "Uno" "$tnick left dcc \(resuming channel message mode\)"
   set UnoDCCIDX($tnick) -1
  }
}

proc unologout:filt {idx text} {
  global UnoChan UnoDCCIDX party-chan party-just-quit
  set hand [idx2hand $idx]
  set party-just-quit $hand
  set tnick [hand2nick $hand]
  if {($tnick == "")||($tnick == "*")} {return $text}
  if {[info exist UnoDCCIDX($tnick)]} {
   unolog "Uno" "$tnick left dcc \(resuming channel message mode\)"
   set UnoDCCIDX($tnick) -1
  }
  return $text
}

# Show all players cards
proc dccunohands {hand idx txt} {
 global UnoHand RoundRobin
 set n 0
 while {$n != [llength $RoundRobin]} {
  set un [lindex $RoundRobin $n]
  unolog $un [UnoSortHand $UnoHand($un)]
  incr n
 }
}

# Rehash configuration
proc dcc_unorehash {hand idx txt} {
 unolog "$hand" "rehashing uno config"
 UnoReadCFG
 return
}

UnoReadCFG
UnoReadScores

putlog "uno.tcl by g0eZ loaded....."

No comments:

Post a Comment