lila/app/templating/GameHelper.scala

264 lines
10 KiB
Scala
Raw Normal View History

2013-02-27 17:12:13 -07:00
package lila.app
2013-03-25 11:52:18 -06:00
package templating
2012-05-17 06:32:25 -06:00
import chess.{ Status => S, Color, Black, White, Clock, Mode }
2013-12-25 12:09:11 -07:00
import controllers.routes
2020-02-11 17:15:44 -07:00
import play.api.i18n.Lang
2013-03-25 11:52:18 -06:00
2020-02-11 17:15:44 -07:00
import lila.api.Context
2019-04-03 21:32:39 -06:00
import lila.app.ui.ScalatagsTemplate._
2019-12-13 07:30:20 -07:00
import lila.game.{ Game, Namer, Player, Pov }
2020-02-11 17:11:50 -07:00
import lila.i18n.{ I18nKeys => trans, defaultLang }
2020-02-09 07:38:26 -07:00
import lila.user.{ Title, User }
2012-05-17 06:32:25 -06:00
2019-04-09 03:21:00 -06:00
trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHelper with ChessgroundHelper =>
2014-02-06 11:22:28 -07:00
2014-02-22 18:15:01 -07:00
def netBaseUrl: String
def cdnUrl(path: String): String
2014-02-22 18:15:01 -07:00
2020-05-05 22:11:15 -06:00
def povOpenGraph(pov: Pov) =
lila.app.ui.OpenGraph(
image = cdnUrl(routes.Export.gameThumbnail(pov.gameId).url).some,
title = titleGame(pov.game),
url = s"$netBaseUrl${routes.Round.watcher(pov.gameId, pov.color.name).url}",
description = describePov(pov)
)
2014-05-26 03:39:47 -06:00
2015-10-07 11:18:48 -06:00
def titleGame(g: Game) = {
2019-12-13 07:30:20 -07:00
val speed = chess.Speed(g.clock.map(_.config)).name
2015-10-07 11:18:48 -06:00
val variant = g.variant.exotic ?? s" ${g.variant.name}"
s"$speed$variant Chess • ${playerText(g.whitePlayer)} vs ${playerText(g.blackPlayer)}"
}
2014-05-26 03:39:47 -06:00
def describePov(pov: Pov) = {
import pov._
val p1 = playerText(player, withRating = true)
val p2 = playerText(opponent, withRating = true)
2016-02-16 07:59:52 -07:00
val speedAndClock =
if (game.imported) "imported"
2019-12-13 07:30:20 -07:00
else
game.clock.fold(chess.Speed.Correspondence.name) { c =>
s"${chess.Speed(c.config).name} (${c.config.show})"
}
2014-05-26 03:39:47 -06:00
val mode = game.mode.name
2019-12-13 07:30:20 -07:00
val variant =
if (game.variant == chess.variant.FromPosition) "position setup chess"
else if (game.variant.exotic) game.variant.name
else "chess"
2014-05-26 03:39:47 -06:00
import chess.Status._
val result = (game.winner, game.loser, game.status) match {
2019-12-13 07:30:20 -07:00
case (Some(w), _, Mate) => s"${playerText(w)} won by checkmate"
2014-06-21 06:56:02 -06:00
case (_, Some(l), Resign | Timeout | Cheat | NoStart) => s"${playerText(l)} resigned"
2019-12-13 07:30:20 -07:00
case (_, Some(l), Outoftime) => s"${playerText(l)} forfeits by time"
case (Some(w), _, UnknownFinish) => s"${playerText(w)} won"
case (_, _, Draw | Stalemate | UnknownFinish) => "Game is a draw"
case (_, _, Aborted) => "Game has been aborted"
case (_, _, VariantEnd) =>
game.variant match {
case chess.variant.KingOfTheHill => "King in the center"
case chess.variant.ThreeCheck => "Three checks"
case chess.variant.Antichess => "Lose all your pieces to win"
case chess.variant.Atomic => "Explode or mate your opponent's king to win"
case chess.variant.Horde => "Destroy the horde to win"
case chess.variant.RacingKings => "Race to the eighth rank to win"
case chess.variant.Crazyhouse => "Drop captured pieces on the board"
case _ => "Variant ending"
}
2014-06-21 06:56:02 -06:00
case _ => "Game is still being played"
2014-05-26 03:39:47 -06:00
}
val moves = s"${game.chess.fullMoveNumber} moves"
2014-05-26 03:39:47 -06:00
s"$p1 plays $p2 in a $mode $speedAndClock game of $variant. $result after $moves. Click to replay, analyse, and discuss the game!"
}
2020-05-05 22:11:15 -06:00
def variantName(variant: chess.variant.Variant)(implicit lang: Lang) =
variant match {
case chess.variant.Standard => trans.standard.txt()
case chess.variant.FromPosition => trans.fromPosition.txt()
case v => v.name
}
2012-05-17 10:49:10 -06:00
2020-02-11 17:11:50 -07:00
def variantNameNoCtx(variant: chess.variant.Variant) = variantName(variant)(defaultLang)
2014-05-21 17:48:12 -06:00
2020-02-11 17:15:44 -07:00
def shortClockName(clock: Option[Clock.Config])(implicit lang: Lang): Frag =
2019-04-24 00:08:30 -06:00
clock.fold[Frag](trans.unlimited())(shortClockName)
2012-06-09 05:30:34 -06:00
2019-04-09 03:46:13 -06:00
def shortClockName(clock: Clock.Config): Frag = raw(clock.show)
2012-06-09 05:30:34 -06:00
2020-05-05 22:11:15 -06:00
def modeName(mode: Mode)(implicit lang: Lang): String =
mode match {
case Mode.Casual => trans.casual.txt()
case Mode.Rated => trans.rated.txt()
}
2012-05-19 07:37:10 -06:00
2020-02-11 17:11:50 -07:00
def modeNameNoCtx(mode: Mode): String = modeName(mode)(defaultLang)
2013-12-25 12:09:11 -07:00
2019-04-22 04:49:26 -06:00
def playerUsername(player: Player, withRating: Boolean = true, withTitle: Boolean = true): Frag =
player.aiLevel.fold[Frag](
player.userId.flatMap(lightUser).fold[Frag](lila.user.User.anonymous) { user =>
2020-08-14 14:50:43 -06:00
frag(
2020-09-09 00:52:27 -06:00
titleTag(user.title ifTrue withTitle map Title.apply),
2020-08-14 14:50:43 -06:00
if (withRating) s"${user.name} (${lila.game.Namer ratingString player})"
else user.name
)
2019-04-22 03:42:25 -06:00
}
2019-12-13 07:30:20 -07:00
) { level =>
raw(s"A.I. level $level")
}
2012-05-17 06:32:25 -06:00
2014-04-27 17:14:31 -06:00
def playerText(player: Player, withRating: Boolean = false) =
2020-01-22 08:35:06 -07:00
Namer.playerTextBlocking(player, withRating)(lightUser)
def gameVsText(game: Game, withRatings: Boolean = false): String =
2020-01-22 08:35:06 -07:00
Namer.gameVsTextBlocking(game, withRatings)(lightUser)
2014-04-23 15:27:09 -06:00
2019-04-22 04:49:26 -06:00
val berserkIconSpan = iconTag("`")
2019-12-13 07:30:20 -07:00
val statusIconSpan = i(cls := "status")
2015-08-18 04:59:44 -06:00
def playerLink(
2019-12-13 07:30:20 -07:00
player: Player,
cssClass: Option[String] = None,
withOnline: Boolean = true,
withRating: Boolean = true,
withDiff: Boolean = true,
engine: Boolean = false,
withStatus: Boolean = false,
withBerserk: Boolean = false,
mod: Boolean = false,
link: Boolean = true
2020-02-11 17:15:44 -07:00
)(implicit lang: Lang): Frag = {
2015-08-18 04:59:44 -06:00
val statusIcon =
2019-04-22 04:49:26 -06:00
if (withStatus) statusIconSpan.some
else if (withBerserk && player.berserk) berserkIconSpan.some
else none
2014-04-16 16:01:24 -06:00
player.userId.flatMap(lightUser) match {
2014-02-18 16:18:22 -07:00
case None =>
val klass = cssClass.??(" " + _)
2019-04-22 04:49:26 -06:00
span(cls := s"user-link$klass")(
(player.aiLevel, player.name) match {
case (Some(level), _) => aiNameFrag(level, withRating)
2019-12-13 07:30:20 -07:00
case (_, Some(name)) => name
case _ => User.anonymous
2019-04-22 04:49:26 -06:00
},
statusIcon
)
2019-12-13 07:30:20 -07:00
case Some(user) =>
frag(
(if (link) a else span)(
cls := userClass(user.id, cssClass, withOnline),
href := s"${routes.User show user.name}${if (mod) "?mod" else ""}"
)(
withOnline option frag(lineIcon(user), " "),
playerUsername(player, withRating),
(player.ratingDiff ifTrue withDiff) map { d =>
frag(" ", showRatingDiff(d))
},
engine option span(
cls := "engine_mark",
title := trans.thisAccountViolatedTos.txt()
2019-12-13 07:30:20 -07:00
)
),
statusIcon
)
2014-02-18 16:18:22 -07:00
}
2012-05-17 06:32:25 -06:00
}
2020-05-05 22:11:15 -06:00
def gameEndStatus(game: Game)(implicit lang: Lang): String =
game.status match {
case S.Aborted => trans.gameAborted.txt()
case S.Mate => trans.checkmate.txt()
case S.Resign =>
game.loser match {
case Some(p) if p.color.white => trans.whiteResigned.txt()
case _ => trans.blackResigned.txt()
}
case S.UnknownFinish => trans.finished.txt()
case S.Stalemate => trans.stalemate.txt()
case S.Timeout =>
game.loser match {
case Some(p) if p.color.white => trans.whiteLeftTheGame.txt()
case Some(_) => trans.blackLeftTheGame.txt()
case None => trans.draw.txt()
}
2020-09-21 01:28:28 -06:00
case S.Draw => trans.draw.txt()
case S.Outoftime =>
(game.turnColor, game.loser) match {
case (White, Some(_)) => trans.whiteTimeOut.txt()
case (White, None) => trans.whiteTimeOut.txt() + " • " + trans.draw.txt()
case (Black, Some(_)) => trans.blackTimeOut.txt()
case (Black, None) => trans.blackTimeOut.txt() + " • " + trans.draw.txt()
}
2020-08-16 06:48:46 -06:00
case S.NoStart =>
2020-05-05 22:11:15 -06:00
val color = game.loser.fold(Color.white)(_.color).name.capitalize
s"$color didn't move"
case S.Cheat => "Cheat detected"
case S.VariantEnd =>
game.variant match {
case chess.variant.KingOfTheHill => trans.kingInTheCenter.txt()
case chess.variant.ThreeCheck => trans.threeChecks.txt()
case chess.variant.RacingKings => trans.raceFinished.txt()
case _ => trans.variantEnding.txt()
}
case _ => ""
}
2012-05-17 07:47:38 -06:00
def gameTitle(game: Game, color: Color): String = {
2014-04-27 17:14:31 -06:00
val u1 = playerText(game player color, withRating = true)
val u2 = playerText(game opponent color, withRating = true)
2019-12-13 07:30:20 -07:00
val clock = game.clock ?? { c =>
" • " + c.config.show
}
2014-08-02 14:03:20 -06:00
val variant = game.variant.exotic ?? s"${game.variant.name}"
2014-08-02 13:27:17 -06:00
s"$u1 vs $u2$clock$variant"
2014-04-26 11:47:28 -06:00
}
2014-06-18 15:16:34 -06:00
// whiteUsername 1-0 blackUsername
def gameSummary(whiteUserId: String, blackUserId: String, finished: Boolean, result: Option[Boolean]) = {
2017-01-14 13:45:03 -07:00
val res = if (finished) chess.Color.showResult(result map Color.apply) else "*"
2014-06-18 15:16:34 -06:00
s"${usernameOrId(whiteUserId)} $res ${usernameOrId(blackUserId)}"
}
def gameResult(game: Game) =
2017-01-14 13:45:03 -07:00
if (game.finished) chess.Color.showResult(game.winnerColor)
else "*"
def gameLink(
2019-12-13 07:30:20 -07:00
game: Game,
color: Color,
ownerLink: Boolean = false,
tv: Boolean = false
2020-02-09 07:38:26 -07:00
)(implicit ctx: Context): String = {
2018-07-20 04:21:06 -06:00
val owner = ownerLink ?? ctx.me.flatMap(game.player)
if (tv) routes.Tv.index()
2019-12-13 07:30:20 -07:00
else
owner.fold(routes.Round.watcher(game.id, color.name)) { o =>
routes.Round.player(game fullIdOf o.color)
}
2018-07-20 04:21:06 -06:00
}.toString
2020-02-09 07:38:26 -07:00
def gameLink(pov: Pov)(implicit ctx: Context): String = gameLink(pov.game, pov.color)
2019-04-05 20:20:44 -06:00
2019-12-08 11:12:00 -07:00
def challengeTitle(c: lila.challenge.Challenge) = {
2018-04-25 20:45:50 -06:00
val speed = c.clock.map(_.config).fold(chess.Speed.Correspondence.name) { clock =>
2016-06-02 11:03:44 -06:00
s"${chess.Speed(clock).name} (${clock.show})"
}
val variant = c.variant.exotic ?? s" ${c.variant.name}"
2020-04-23 20:03:47 -06:00
val challenger = c.challengerUser.fold(User.anonymous) { reg =>
s"${usernameOrId(reg.id)} (${reg.rating.show})"
}
2020-04-26 14:52:01 -06:00
val players =
if (c.isOpen) "Open challenge"
else
c.destUser.fold(s"Challenge from $challenger") { dest =>
s"$challenger challenges ${usernameOrId(dest.id)} (${dest.rating.show})"
}
2016-06-02 11:03:44 -06:00
s"$speed$variant ${c.mode.name} Chess • $players"
}
2019-12-08 11:12:00 -07:00
def challengeOpenGraph(c: lila.challenge.Challenge) =
2016-06-02 11:03:44 -06:00
lila.app.ui.OpenGraph(
title = challengeTitle(c),
url = s"$netBaseUrl${routes.Round.watcher(c.id, chess.White.name).url}",
description = "Join the challenge or watch the game here."
)
2012-05-17 06:32:25 -06:00
}