|  | @@ -3,9 +3,19 @@ package models
 | 
	
		
			
				|  |  |  import akka.actor._
 | 
	
		
			
				|  |  |  import scala.concurrent.duration._
 | 
	
		
			
				|  |  |  import play.api.libs.concurrent._
 | 
	
		
			
				|  |  | +import play.api.libs.json._
 | 
	
		
			
				|  |  |  import play.api.Play.current
 | 
	
		
			
				|  |  |  import play.api.libs.concurrent.Execution.Implicits._
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +object Card {
 | 
	
		
			
				|  |  | +  implicit val writes = new Writes[Card] {
 | 
	
		
			
				|  |  | +    def writes(o: Card) = Json.obj(
 | 
	
		
			
				|  |  | +      "word" -> o.word,
 | 
	
		
			
				|  |  | +      "taboo" -> o.taboo
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  case class Card(word: String, taboo: Set[String]) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def isTaboo(text: String) = {
 | 
	
	
		
			
				|  | @@ -24,6 +34,16 @@ case class Card(word: String, taboo: Set[String]) {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +object Team {
 | 
	
		
			
				|  |  | +  implicit val writes = new Writes[Team] {
 | 
	
		
			
				|  |  | +    def writes(o: Team) = Json.obj(
 | 
	
		
			
				|  |  | +      "members" -> o.members,
 | 
	
		
			
				|  |  | +      "player" -> o.player,
 | 
	
		
			
				|  |  | +      "guessers" -> o.guessers
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  class Team {
 | 
	
		
			
				|  |  |    var members = List.empty[String]
 | 
	
		
			
				|  |  |    var player = ""
 | 
	
	
		
			
				|  | @@ -53,23 +73,29 @@ class Team {
 | 
	
		
			
				|  |  |    def size = members.size
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +object Round {
 | 
	
		
			
				|  |  | +  implicit val writes = new Writes[Round] {
 | 
	
		
			
				|  |  | +    def writes(o: Round) = Json.obj(
 | 
	
		
			
				|  |  | +      "team" -> o.team,
 | 
	
		
			
				|  |  | +      "monitors" -> o.monitors
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  case class Round(team: Team, monitors: Set[String])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  case class Information(text: String)
 | 
	
		
			
				|  |  |  case class Guess(username: String, text: String)
 | 
	
		
			
				|  |  |  case object Pass
 | 
	
		
			
				|  |  |  case class Taboo(username: String)
 | 
	
		
			
				|  |  | +case object End
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -case class Correct(username: String, card: Card)
 | 
	
		
			
				|  |  | -case class Invalid(card: Card)
 | 
	
		
			
				|  |  | -case class Passed(card: Card)
 | 
	
		
			
				|  |  | -case class Tabooed(username: String, card: Card)
 | 
	
		
			
				|  |  | +case class Score(kind: String, points: Int, card: Card, username: String = "")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  case object NextCard
 | 
	
		
			
				|  |  |  case object PrepRound
 | 
	
		
			
				|  |  |  case object StartRound
 | 
	
		
			
				|  |  | -case object End
 | 
	
		
			
				|  |  | -case class Points(points: Int)
 | 
	
		
			
				|  |  | +case class EndRound(points: Int, card: Option[Card])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class TabooGame(val chatActor: ActorRef) extends Actor {
 | 
	
		
			
				|  |  |    var ready = false
 | 
	
	
		
			
				|  | @@ -90,6 +116,7 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
 | 
	
		
			
				|  |  |        else {
 | 
	
		
			
				|  |  |          teamB.join(username)
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +      announceStatus("join", username)
 | 
	
		
			
				|  |  |        if(round.isEmpty) {
 | 
	
		
			
				|  |  |          self ! PrepRound
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -97,10 +124,18 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
 | 
	
		
			
				|  |  |      case Quit(username) =>
 | 
	
		
			
				|  |  |        teamA.leave(username)
 | 
	
		
			
				|  |  |        teamB.leave(username)
 | 
	
		
			
				|  |  | -      if(!round.isEmpty && (teamA.size < 2 || (currentTeam == teamB && teamB.size < 2))) {
 | 
	
		
			
				|  |  | -        roundActor ! End
 | 
	
		
			
				|  |  | +      announceStatus("quit", username)
 | 
	
		
			
				|  |  | +      if(teamA.size < 2 || currentTeam == teamB && teamB.size < 2) {
 | 
	
		
			
				|  |  | +        if(ready) {
 | 
	
		
			
				|  |  | +          ready = false
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        if(!round.isEmpty) {
 | 
	
		
			
				|  |  | +          roundActor ! End
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    case Talk(username, "/status") => announceStatus()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      case Talk(username, text) => round match {
 | 
	
		
			
				|  |  |        case Some(round) =>
 | 
	
		
			
				|  |  |          if(username == round.team.player) {
 | 
	
	
		
			
				|  | @@ -124,33 +159,26 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    case Correct(username, card) =>
 | 
	
		
			
				|  |  | -      nextCard(username+" got it right!", card)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    case Invalid(card) =>
 | 
	
		
			
				|  |  | -      nextCard("Uh-uh! "+player+" said a taboo word. Sorry.", card)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    case Passed(card) =>
 | 
	
		
			
				|  |  | -      nextCard(player+" has passed.", card)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    case Tabooed(username, card) =>
 | 
	
		
			
				|  |  | -      nextCard("Oh no! "+player+" apparently said a taboo word. :(", card)
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    case Points(points) =>
 | 
	
		
			
				|  |  | -      chatActor ! Talk("*GM", "The round is over. The team got "+points)
 | 
	
		
			
				|  |  | -      round = None
 | 
	
		
			
				|  |  | -      ready = false
 | 
	
		
			
				|  |  | -      self ! PrepRound
 | 
	
		
			
				|  |  | +    case Score(kind, points, card, user) =>
 | 
	
		
			
				|  |  | +      chatActor ! Announce(Json.obj(
 | 
	
		
			
				|  |  | +        "kind" -> "point",
 | 
	
		
			
				|  |  | +        "action" -> kind,
 | 
	
		
			
				|  |  | +        "card" -> card,
 | 
	
		
			
				|  |  | +        "user" -> user
 | 
	
		
			
				|  |  | +      ))
 | 
	
		
			
				|  |  | +      self ! NextCard
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case NextCard =>
 | 
	
		
			
				|  |  |        val card = randomCard()
 | 
	
		
			
				|  |  |        roundActor ! card
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +      val message = Json.obj(
 | 
	
		
			
				|  |  | +        "kind" -> "card",
 | 
	
		
			
				|  |  | +        "card" -> card
 | 
	
		
			
				|  |  | +      )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |        (round.get.monitors + player).foreach { user =>
 | 
	
		
			
				|  |  | -        chatActor ! Tell(
 | 
	
		
			
				|  |  | -          "*GM",
 | 
	
		
			
				|  |  | -          "The word is "+card.word+". The taboo words are: "+card.taboo.reduceLeft(_+" "+_),
 | 
	
		
			
				|  |  | -          user)
 | 
	
		
			
				|  |  | +        chatActor ! Tell(user, message)
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case PrepRound =>
 | 
	
	
		
			
				|  | @@ -168,24 +196,44 @@ class TabooGame(val chatActor: ActorRef) extends Actor {
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |          currentTeam.nextPlayer()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        chatActor ! Talk("*GM", "Next round, the player will be "+currentTeam.player)
 | 
	
		
			
				|  |  | -        chatActor ! Tell("*GM", "Type /start to start the round", currentTeam.player)
 | 
	
		
			
				|  |  | +        chatActor ! Announce(Json.obj(
 | 
	
		
			
				|  |  | +          "kind" -> "roundReady",
 | 
	
		
			
				|  |  | +          "player" -> currentTeam.player
 | 
	
		
			
				|  |  | +        ))
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case StartRound =>
 | 
	
		
			
				|  |  |        round = Some(Round(currentTeam, opposingTeam.members.toSet))
 | 
	
		
			
				|  |  |        roundActor = context.actorOf(Props[TabooRound])
 | 
	
		
			
				|  |  |        Akka.system.scheduler.scheduleOnce(1 minute, roundActor, End)
 | 
	
		
			
				|  |  | +      chatActor ! Announce(Json.obj(
 | 
	
		
			
				|  |  | +        "kind" -> "roundStart"
 | 
	
		
			
				|  |  | +      ))
 | 
	
		
			
				|  |  |        self ! NextCard
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    case EndRound(points, card) =>
 | 
	
		
			
				|  |  | +      chatActor ! Announce(Json.obj(
 | 
	
		
			
				|  |  | +        "kind" -> "roundEnd",
 | 
	
		
			
				|  |  | +        "points" -> points,
 | 
	
		
			
				|  |  | +        "card" -> card
 | 
	
		
			
				|  |  | +      ))
 | 
	
		
			
				|  |  | +      round = None
 | 
	
		
			
				|  |  | +      ready = false
 | 
	
		
			
				|  |  | +      self ! PrepRound
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def player = round.get.team.player
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    def randomCard() = Card("test", Set("a", "b", "c", "d", "e"))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  def nextCard(message: String, oldCard: Card) = {
 | 
	
		
			
				|  |  | -    chatActor ! Talk("*GM", message+" The word was "+oldCard.word+".")
 | 
	
		
			
				|  |  | -    self ! NextCard
 | 
	
		
			
				|  |  | +  def announceStatus(kind: String = "status", user: String = "*GM") {
 | 
	
		
			
				|  |  | +    chatActor ! Announce(Json.obj(
 | 
	
		
			
				|  |  | +      "kind" -> kind,
 | 
	
		
			
				|  |  | +      "user" -> user,
 | 
	
		
			
				|  |  | +      "teamA" -> teamA,
 | 
	
		
			
				|  |  | +      "teamB" -> teamB
 | 
	
		
			
				|  |  | +    ))
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    self ! PrepRound
 | 
	
	
		
			
				|  | @@ -202,7 +250,7 @@ class TabooRound extends Actor {
 | 
	
		
			
				|  |  |      case Guess(username, text) => card.map { card =>
 | 
	
		
			
				|  |  |        if(card.isCorrect(text)) {
 | 
	
		
			
				|  |  |          points += 1
 | 
	
		
			
				|  |  | -        sender ! Correct(username, card)
 | 
	
		
			
				|  |  | +        sender ! Score("correct", points, card, username)
 | 
	
		
			
				|  |  |          this.card = None
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
	
		
			
				|  | @@ -210,25 +258,25 @@ class TabooRound extends Actor {
 | 
	
		
			
				|  |  |      case Information(text) => card.map { card =>
 | 
	
		
			
				|  |  |        if(card.isTaboo(text)) {
 | 
	
		
			
				|  |  |          points -= 1
 | 
	
		
			
				|  |  | -        sender ! Invalid(card)
 | 
	
		
			
				|  |  | +        sender ! Score("invalid", points, card)
 | 
	
		
			
				|  |  |          this.card = None
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case Pass => card.map { card =>
 | 
	
		
			
				|  |  |        points -= 1
 | 
	
		
			
				|  |  | -      sender ! Passed(card)
 | 
	
		
			
				|  |  | +      sender ! Score("pass", points, card)
 | 
	
		
			
				|  |  |        this.card = None
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case Taboo(username) => card.map { card =>
 | 
	
		
			
				|  |  |        points -= 1
 | 
	
		
			
				|  |  | -      sender ! Tabooed(username, card)
 | 
	
		
			
				|  |  | +      sender ! Score("taboo", points, card)
 | 
	
		
			
				|  |  |        this.card = None
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      case End =>
 | 
	
		
			
				|  |  | -      sender ! Points(points)
 | 
	
		
			
				|  |  | +      sender ! EndRound(points, card)
 | 
	
		
			
				|  |  |        context.stop(self)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    }
 |