284 lines
No EOL
9.5 KiB
Python
284 lines
No EOL
9.5 KiB
Python
import random
|
|
|
|
import logging
|
|
|
|
import card as c
|
|
from datetime import datetime
|
|
|
|
from telegram import Message, Chat
|
|
|
|
from config import TIME_REMOVAL_AFTER_SKIP, MIN_FAST_TURN_TIME, SCORE_WIN
|
|
from errors import DeckEmptyError, NotEnoughPlayersError
|
|
from internationalization import __, _
|
|
from shared_vars import gm
|
|
from user_setting import UserSetting
|
|
from utils import send_async, display_name, game_is_running, TIMEOUT
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class Countdown(object):
|
|
player = None
|
|
job_queue = None
|
|
|
|
def __init__(self, player, job_queue):
|
|
self.player = player
|
|
self.job_queue = job_queue
|
|
|
|
|
|
# TODO do_skip() could get executed in another thread (it can be a job), so it looks like it can't use game.translate?
|
|
def do_skip(bot, player, job_queue=None):
|
|
game = player.game
|
|
chat = game.chat
|
|
skipped_player = game.current_player
|
|
next_player = game.current_player.next
|
|
|
|
if skipped_player.waiting_time > 0:
|
|
skipped_player.anti_cheat += 1
|
|
skipped_player.waiting_time -= TIME_REMOVAL_AFTER_SKIP
|
|
if (skipped_player.waiting_time < 0):
|
|
skipped_player.waiting_time = 0
|
|
|
|
try:
|
|
skipped_player.draw()
|
|
except DeckEmptyError:
|
|
pass
|
|
|
|
n = skipped_player.waiting_time
|
|
send_async(bot, chat.id,
|
|
text="Waiting time to skip this player has "
|
|
"been reduced to {time} seconds.\n"
|
|
"Next player: {name}"
|
|
.format(time=n,
|
|
name=display_name(next_player.user))
|
|
)
|
|
logger.info("{player} was skipped!. "
|
|
.format(player=display_name(player.user)))
|
|
game.turn()
|
|
if job_queue:
|
|
start_player_countdown(bot, game, job_queue)
|
|
|
|
else:
|
|
try:
|
|
gm.leave_game(skipped_player.user, chat)
|
|
send_async(bot, chat.id,
|
|
text="{name1} ran out of time "
|
|
"and has been removed from the game!\n"
|
|
"Next player: {name2}"
|
|
.format(name1=display_name(skipped_player.user),
|
|
name2=display_name(next_player.user)))
|
|
logger.info("{player} was skipped!. "
|
|
.format(player=display_name(player.user)))
|
|
if job_queue:
|
|
start_player_countdown(bot, game, job_queue)
|
|
|
|
except NotEnoughPlayersError:
|
|
send_async(bot, chat.id,
|
|
text="{name} ran out of time "
|
|
"and has been removed from the game!\n"
|
|
"The game ended."
|
|
.format(name=display_name(skipped_player.user)))
|
|
|
|
gm.end_game(chat, skipped_player.user)
|
|
|
|
|
|
|
|
def do_play_card(bot, player, result_id):
|
|
"""Plays the selected card and sends an update to the group if needed"""
|
|
card = c.from_str(result_id)
|
|
player.play(card)
|
|
game = player.game
|
|
chat = game.chat
|
|
user = player.user
|
|
|
|
us = UserSetting.get(id=user.id)
|
|
if not us:
|
|
us = UserSetting(id=user.id)
|
|
|
|
if us.stats:
|
|
us.cards_played += 1
|
|
|
|
if game.choosing_color:
|
|
send_async(bot, chat.id, text=_("Please choose a color"))
|
|
|
|
if len(player.cards) == 1:
|
|
send_async(bot, chat.id, text="UNO!")
|
|
|
|
if len(player.cards) == 0:
|
|
send_async(bot, chat.id,
|
|
text=__("{name} won!", multi=game.translate)
|
|
.format(name=user.first_name))
|
|
|
|
if game.mode == "score":
|
|
game.add_score(player)
|
|
score = sum([n[0] for n in game.last_round_score])
|
|
bot.sendMessage(chat.id,
|
|
text=__("Added {pt} point",
|
|
"Added {pt} points",
|
|
score)
|
|
.format(pt=score))
|
|
bot.sendMessage(
|
|
chat.id,
|
|
text=", ".join(
|
|
["{cl} {vl} (+{pt})"
|
|
.format(cl=c.COLOR_ICONS[n[1].color],
|
|
vl=n[1].value or n[1].special,
|
|
pt=n[0])
|
|
for n in sorted(game.last_round_score,
|
|
key=lambda x: x[0])]))
|
|
bot.sendMessage(
|
|
chat.id,
|
|
text=__("Current standings:\n\n") +
|
|
"\n".join([
|
|
"{no}. {name}: {pt}".format(
|
|
no=i + 1,
|
|
name=x[0].user.first_name,
|
|
pt=x[1])
|
|
for i, x in enumerate(
|
|
sorted(game.get_scores(),
|
|
key=lambda x: x[1],
|
|
reverse=True))]))
|
|
bot.sendMessage(
|
|
chat.id,
|
|
text=__("Points to win: {score}").format(score=game.win_score))
|
|
|
|
players_cache = game.players
|
|
highest = sorted(zip(players_cache, map(
|
|
game.get_score, players_cache
|
|
)), key=lambda x: x[1])[-1]
|
|
if highest[1] >= game.win_score:
|
|
send_async(bot, chat.id,
|
|
text=__("Game ended! {name} wins the game!",
|
|
multi=game.translate)
|
|
.format(highest[0].user.first_name))
|
|
if us.stats:
|
|
us.first_places += 1
|
|
for player in players_cache:
|
|
us_ = UserSetting.get(id=player.user.id)
|
|
if not us:
|
|
us_ = UserSetting(id=player.user.id)
|
|
us_.games_played += 1
|
|
else:
|
|
game.reset_cards()
|
|
game.new_round()
|
|
next_round_message = (
|
|
__("Next round!", multi=game.translate))
|
|
next_player_message = (
|
|
__("First player: {name}\n",
|
|
multi=game.translate)
|
|
.format(name=display_name(game.current_player.user)))
|
|
|
|
bot.sendMessage(chat.id,
|
|
text=next_round_message,
|
|
timeout=TIMEOUT)
|
|
bot.sendSticker(chat.id,
|
|
sticker=c.STICKERS[str(game.last_card)],
|
|
timeout=TIMEOUT)
|
|
bot.sendMessage(chat.id,
|
|
text=next_player_message,
|
|
timeout=TIMEOUT)
|
|
return
|
|
# If game mode is "score", 'do_play_card' should stop here
|
|
|
|
if us.stats:
|
|
us.games_played += 1
|
|
|
|
if game.players_won is 0:
|
|
us.first_places += 1
|
|
|
|
game.players_won += 1
|
|
|
|
try:
|
|
gm.leave_game(user, chat)
|
|
except NotEnoughPlayersError:
|
|
send_async(bot, chat.id,
|
|
text=__("Game ended!", multi=game.translate))
|
|
|
|
us2 = UserSetting.get(id=game.current_player.user.id)
|
|
if us2 and us2.stats:
|
|
us2.games_played += 1
|
|
|
|
gm.end_game(chat, user)
|
|
|
|
|
|
def do_draw(bot, player):
|
|
"""Does the drawing"""
|
|
game = player.game
|
|
draw_counter_before = game.draw_counter
|
|
|
|
try:
|
|
player.draw()
|
|
except DeckEmptyError:
|
|
send_async(bot, player.game.chat.id,
|
|
text=__("There are no more cards in the deck.",
|
|
multi=game.translate))
|
|
|
|
if (game.last_card.value == c.DRAW_TWO or
|
|
game.last_card.special == c.DRAW_FOUR) and \
|
|
draw_counter_before > 0:
|
|
game.turn()
|
|
|
|
|
|
def do_call_bluff(bot, player):
|
|
"""Handles the bluff calling"""
|
|
game = player.game
|
|
chat = game.chat
|
|
|
|
if player.prev.bluffing:
|
|
send_async(bot, chat.id,
|
|
text=__("Bluff called! Giving 4 cards to {name}",
|
|
multi=game.translate)
|
|
.format(name=player.prev.user.first_name))
|
|
|
|
try:
|
|
player.prev.draw()
|
|
except DeckEmptyError:
|
|
send_async(bot, player.game.chat.id,
|
|
text=__("There are no more cards in the deck.",
|
|
multi=game.translate))
|
|
|
|
else:
|
|
game.draw_counter += 2
|
|
send_async(bot, chat.id,
|
|
text=__("{name1} didn't bluff! Giving 6 cards to {name2}",
|
|
multi=game.translate)
|
|
.format(name1=player.prev.user.first_name,
|
|
name2=player.user.first_name))
|
|
try:
|
|
player.draw()
|
|
except DeckEmptyError:
|
|
send_async(bot, player.game.chat.id,
|
|
text=__("There are no more cards in the deck.",
|
|
multi=game.translate))
|
|
|
|
game.turn()
|
|
|
|
|
|
def start_player_countdown(bot, game, job_queue):
|
|
player = game.current_player
|
|
time = player.waiting_time
|
|
|
|
if time < MIN_FAST_TURN_TIME:
|
|
time = MIN_FAST_TURN_TIME
|
|
|
|
if game.mode == 'fast':
|
|
if game.job:
|
|
game.job.schedule_removal()
|
|
|
|
job = job_queue.run_once(
|
|
#lambda x,y: do_skip(bot, player),
|
|
skip_job,
|
|
time,
|
|
context=Countdown(player, job_queue)
|
|
)
|
|
|
|
logger.info("Started countdown for player: {player}. {time} seconds."
|
|
.format(player=display_name(player.user), time=time))
|
|
player.game.job = job
|
|
|
|
|
|
def skip_job(bot, job):
|
|
player = job.context.player
|
|
game = player.game
|
|
if game_is_running(game):
|
|
job_queue = job.context.job_queue
|
|
do_skip(bot, player, job_queue) |