Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- CHALLENGE_TIMEOUT <- 30;
- class TTTChallenge {
- challenger = null;
- challengee = null;
- startTime = 0;
- constructor (challenger, challengee, startTime) {
- this.challenger = challenger;
- this.challengee = challengee;
- this.startTime = startTime;
- }
- }
- class TTTChallengeManager {
- challenges = null;
- main = null;
- constructor(main) {
- this.main = main;
- this.challenges = {};
- }
- function MakeChallenge(challenger, challengee) {
- if (main.gameManager.IsPlaying(challenger)) {
- ::ClientMessage("You are currently in the middle of a game.", challenger, 255, 255, 255);
- return;
- } else if (challenger.ID == challengee.ID) {
- ::ClientMessage("You cannot challenge yourself.", challenger, 255, 255, 255);
- return;
- } else if (main.gameManager.IsPlaying(challengee)) {
- ::ClientMessage("That player is currently in the middle of a game.", challenger, 255, 255, 255);
- return;
- } else if (challenger.ID in challenges) {
- if (challenges[challenger.ID].challengee.ID == challengee.ID) {
- ::ClientMessage("Already sent a challenge to that player.", challenger, 255, 255, 255);
- return;
- } else {
- WithdrawChallenge(challenger);
- }
- }
- challenges[challenger.ID] <- TTTChallenge(challenger, challengee, ::time());
- ::ClientMessage("You have challenged " + challengee.Name + " to a game of tic-tac-toe.", challenger, 255, 255, 255);
- ::ClientMessage(challenger.Name + " has challenged you to a game of tic-tac-toe. Enter '/accept " + challenger.ID + "' to accept.", challengee, 255, 255, 255);
- }
- function AcceptChallenge(challengee, challenger) {
- if (!(challenger.ID in challenges) || challenges[challenger.ID].challengee.ID != challengee.ID) {
- ::ClientMessage("That player has not challenged you.", challengee, 255, 255, 255);
- return;
- }
- local game = main.gameManager.CreateGame(challenger, challengee);
- if (game == null) {
- ::ClientMessage(challengee.Name + " tried to accept your challenge, but slots are full at the moment.", challenger, 255, 255, 255);
- ::ClientMessage("You tried to accept " + challenger.Name + "'s challenge, but slots are full at the moment.", challengee, 255, 255, 255);
- } else {
- ::ClientMessage(challengee.Name + " has accepted your challenge.", challenger, 255, 255, 255);
- ::ClientMessage("You have accepted " + challenger.Name + "'s challenge.", challengee, 255, 255, 255);
- delete challenges[challenger.ID];
- game.Begin();
- }
- }
- function WithdrawChallenge(challenger) {
- if (challenger.ID in challenges) {
- delete challenges[challenger.ID];
- ::ClientMessage("You have withdrawn your challenge.", challenger, 255, 255, 255);
- ::ClientMessage(challenger.Name + " has withdrawn his challenge to you.", challengee, 255, 255, 255);
- } else {
- ::ClientMessage("You have not challenged anyone.", challenger, 255, 255, 255);
- }
- }
- function RemoveExpiredChallenges() {
- local deleteList = array(0);
- local currentTime = ::time();
- foreach (challengerId, challenge in challenges) {
- if (challenge.startTime + CHALLENGE_TIMEOUT < currentTime) {
- deleteList.append(challengerId);
- ::ClientMessage("Your challenge has expired.", challenge.challenger, 255, 255, 255);
- ::ClientMessage(challenge.challenger.Name + "'s challenge to you has expired.", challenge.challengee, 255, 255, 255);
- }
- }
- DeleteChallenges(deleteList)
- }
- function DeleteChallenges(deleteList) {
- foreach (challengerId in deleteList) {
- delete challenges[challengerId];
- }
- }
- function PlayerQuit(player) {
- local deleteList = array(0);
- foreach (challengerId, challenge in challenges) {
- if (challenge.challengee.ID == player.ID) {
- ::ClientMessage("Your challengee has quit the game.", challenge.challenger, 255, 255, 255);
- deleteList.append(challengerId);
- } else if (challenge.challenger.ID == player.ID) {
- ::ClientMessage(challenge.challenger.Name + "'s challenge to you has been cancelled.", challenge.challengee, 255, 255, 255);
- deleteList.append(challengerId);
- }
- }
- DeleteChallenges(deleteList);
- }
- }
- GAME_SLOTS <- 3;
- BETTING_TIME <- 20;
- MOVE_MAX_TIME <- 15;
- GAME_DISAPPEAR_TIME <- 5;
- class TTTGame {
- players = null;
- pots = null;
- board = null;
- slot = -1;
- bettingStage = false;
- startTime = 0;
- lastMoveTime = 0;
- turn = 1;
- turnCount = 0;
- endTime = 0;
- grid = null;
- gridSprites = null;
- potInfo = null;
- potFree = null;
- labels = null;
- static winPatterns = [[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6]];
- constructor(challenger, challengee, slot) {
- this.players = [challenger, challengee];
- this.pots = [0, 0];
- this.board = null;
- this.slot = slot;
- this.bettingStage = true;
- this.startTime = 0;
- this.lastMoveTime = 0;
- this.turn = 1;
- this.turnCount = 0;
- this.endTime = 0;
- this.grid = array(9, -1);
- this.gridSprites = array(9, null);
- this.potInfo = [{}, {}];
- this.potFree = [0, 0];
- this.labels = [null, null];
- }
- function HasPlayer(player) {
- return player.ID == players[0].ID || player.ID == players[1].ID;
- }
- function Begin() {
- board = ::CreateSprite("board.png", 520 + 320 * slot, 420, 0, 0, 0, 255);
- board.ShowForAll();
- bettingStage = true;
- startTime = ::time();
- ::ClientMessageToAll(players[0].Name + " and " + players[1].Name + " will start playing tic-tac-toe in " + BETTING_TIME + " seconds.", 255, 255, 255);
- ::ClientMessageToAll("Type '/bet " + slot + " A <sum>' to bet for " + players[0].Name + " or '/bet " + slot + " B <sum>' to bet for " + players[1].Name + ".", 255, 255, 255);
- UpdateLabels();
- }
- function AddBet(player, side, sum) {
- if (!bettingStage) {
- ::ClientMessage("Betting for this game is over.", player, 255, 255, 255);
- } else if (sum <= 0) {
- ::ClientMessage("You have to bet at least $1.", player, 255, 255, 255);
- } else if (player.Money < sum) {
- ::ClientMessage("You don't have that much money on you.", player, 255, 255, 255);
- } else {
- ::ClientMessage("You bet $" + sum + " on " + players[side].Name + ".", player, 255, 255, 255);
- player.GiveMoney(-sum);
- pots[side] += sum;
- if (!(player.ID in potInfo[side])) {
- potInfo[side][player.ID] <- 0;
- }
- potInfo[side][player.ID] += sum;
- UpdateLabels();
- }
- }
- function DistributePots(winner) {
- local winSide = (winner == null) ? -1 : (winner.ID == players[0].ID ? 0 : 1);
- if (winSide == -1) {
- local moneyBack = {};
- for (local i = 0; i < 2; i++) {
- foreach (playerId, sum in potInfo[i]) {
- if (!(playerId in moneyBack)) {
- moneyBack[playerId] <- 0;
- }
- moneyBack[playerId] += sum;
- }
- }
- foreach (playerId, sum in moneyBack) {
- local player = FindPlayer(playerId);
- ::ClientMessage("You received your money ($" + sum + ") back due to a draw (" + players[0].Name + " vs + " + players[1].Name + ").", player, 255, 255, 255);
- player.GiveMoney(sum);
- }
- local share = potFree[0] + potFree[1];
- if (share > 0) {
- for (local i = 0; i < 2; i++) {
- ::ClientMessage("You receive half the money ($" + share + ") of bets by disconnected players.", players[i], 255, 255, 255);
- players[i].GiveMoney(share);
- }
- }
- } else {
- local ownedPots = pots[winSide] - potFree[winSide];
- local allMoney = pots[0] + pots[1];
- if (ownedPots == 0) {
- if (allMoney > 0) {
- ::ClientMessage("You receive all betted money ($" + allMoney + ") since no connected player bet for you.", players[winSide], 255, 255, 255);
- players[winSide].GiveMoney(allMoney);
- }
- } else {
- local winnerMoney = (0.05 * allMoney.tofloat()).tointeger();
- ::ClientMessage("As the winner, you receive 5% of betted money ($" + winnerMoney + ").", players[winSide], 255, 255, 255);
- players[winSide].GiveMoney(winnerMoney);
- local sharedMoney = (0.95 * allMoney.tofloat()).tointeger();
- local fraction = {};
- foreach (playerId, sum in potInfo[winSide]) {
- local fraction = sum.tofloat() / ownedPots.tofloat();
- local winnings = (sharedMoney.tofloat() * fraction).tointeger();
- local player = FindPlayer(playerId);
- ::ClientMessage("You receive $" + winnings + " in winnings (" + players[0].Name + " vs " + players[1].Name + ").", player, 255, 255, 255);
- player.GiveMoney(winnings);
- }
- }
- }
- }
- function UnlinkPlayerBets(player) {
- for (local i = 0; i < 2; i++) {
- if (player.ID in potInfo[i]) {
- potFree[i] += potInfo[i][player.ID];
- delete potInfo[i][player.ID];
- }
- }
- }
- function UpdateLabels() {
- for (local i = 0; i < 2; i++) {
- if (labels[i] != null) {
- labels[i].Delete();
- }
- labels[i] = ::CreateTextdraw((turn == i ? "> " : "") + players[i].Name + " ($" + pots[i] + ")", 520 + 320 * slot + 10, 400 + i * 260, turn == i ? 0xFFFF5555 : 0xFFFFFFFF);
- labels[i].ShowForAll();
- }
- }
- function DrawGridSprite(cell, image) {
- local cellx = cell % 3
- local celly = cell / 3
- gridSprites[cell] = ::CreateSprite(image == 0 ? "cello.png" : "cellx.png", 520 + 320 * slot + cellx * 80, 420 + celly * 80, 0, 0, 0, 255);
- gridSprites[cell].ShowForAll();
- }
- function MakeTurn(player, cell) {
- if (bettingStage) {
- local timeLeft = BETTING_TIME - (::time() - startTime);
- ::ClientMessage("Wait till bets are over (" + timeLeft + " more seconds).", player, 255, 255, 255);
- } else if (endTime != 0) {
- ::ClientMessage("The game has ended. You will be able to challenge other players in a few seconds.", player, 255, 255, 255);
- } else if (players[turn].ID != player.ID) {
- ::ClientMessage("It is not your turn.", player, 255, 255, 255);
- } else if(cell < 0 || cell > 8) {
- ::ClientMessage("Invalid cell.", player, 255, 255, 255);
- } else if(grid[cell] != -1) {
- ::ClientMessage("That cell is not empty.", player, 255, 255, 255);
- } else {
- ::ClientMessage("You made a move.", player, 255, 255, 255);
- lastMoveTime = ::time();
- turnCount += 1;
- grid[cell] = turn;
- DrawGridSprite(cell, turn);
- CheckGameState();
- turn = 1 - turn;
- UpdateLabels();
- }
- }
- function CheckGameState() {
- foreach (p in winPatterns) {
- if (grid[p[0]] == grid[p[1]] && grid[p[0]] == grid[p[2]] && grid[p[0]] != -1) {
- Victory(players[grid[p[0]]]);
- return;
- }
- }
- if (turnCount == 9) {
- Victory(null);
- }
- }
- function Process() {
- local currentTime = ::time();
- if (bettingStage) {
- if (startTime + BETTING_TIME <= currentTime) {
- ::ClientMessageToAll("Betting for match " + players[0].Name + " ($" + pots[0] + ") vs " + players[1].Name + " ($" + pots[1] + ") has ended. The game will begin.", 255, 255, 255);
- bettingStage = false;
- lastMoveTime = currentTime;
- }
- } else if (endTime == 0) {
- if (lastMoveTime + MOVE_MAX_TIME <= currentTime) {
- local winner = 1 - turn;
- ::ClientMessage(players[1 - winner].Name + " has forfeited the match by taking longer than " + MOVE_MAX_TIME + " seconds.", players[winner], 255, 255, 255);
- ::ClientMessage("You lost the game because you didn't make a move in " + MOVE_MAX_TIME + " seconds.", players[1 - winner], 255, 255, 255);
- Victory(players[winner]);
- }
- } else if (endTime + GAME_DISAPPEAR_TIME <= currentTime) {
- CleanUp();
- return false;
- }
- return true;
- }
- function Victory(player) {
- endTime = ::time();
- if (player == null) {
- ::ClientMessageToAll("The match between " + players[0].Name + " and " + players[1].Name + " has ended with a draw.", 255, 255, 255);
- } else {
- local winner = player.ID == players[0].ID ? 0 : 1;
- ::ClientMessageToAll("The match between " + players[0].Name + " and " + players[1].Name + " has ended. " + players[winner].Name + " won.", 255, 255, 255);
- }
- DistributePots(player);
- }
- function PlayerQuit(player) {
- UnlinkPlayerBets(player);
- for (local i = 0; i < 2; i++) {
- if (players[i].ID == player.ID) {
- ::ClientMessage(player.Name + " has forfeited the match by quitting.", players[1 - i], 255, 255, 255);
- Victory(players[1 - i]);
- CleanUp();
- return true;
- }
- }
- return false;
- }
- function CleanUp() {
- if (board != null) {
- board.Delete();
- board = null;
- }
- foreach(i, sprite in gridSprites) {
- if (sprite != null) {
- sprite.Delete();
- gridSprites[i] = null;
- }
- }
- foreach(i, label in labels) {
- if (label != null) {
- label.Delete();
- labels[i] = null;
- }
- }
- }
- }
- class TTTGameManager {
- games = null;
- main = null;
- binds = null;
- constructor(main) {
- this.main = main;
- this.games = array(GAME_SLOTS, null);
- this.binds = array(9, -1);
- for(local i = 0; i < 9; i++) {
- this.binds[i] = ::BindKey(false, 49 + i, 0, 0);
- }
- }
- function IsPlaying(player) {
- return GetPlayerGame(player) != null;
- }
- function GetPlayerGame(player) {
- foreach (game in games) {
- if (game != null && game.HasPlayer(player)) {
- return game;
- }
- }
- return null;
- }
- function CreateGame(challenger, challengee) {
- foreach (i, game in games) {
- if (game == null) {
- games[i] = TTTGame(challenger, challengee, i);
- return games[i];
- }
- }
- return null;
- }
- function MakeBet(player, game, side, sum) {
- if (game < 0 || game >= GAME_SLOTS || games[game] == null) {
- ::ClientMessage("No such game.", player, 255, 255, 255);
- } else {
- games[game].AddBet(player, side, sum);
- }
- }
- function PlayerQuit(player) {
- foreach (i, game in games) {
- if (game != null) {
- if (game.PlayerQuit(player)) {
- games[i] = null;
- }
- }
- }
- }
- function MoveHandler(player, cell) {
- local game = GetPlayerGame(player);
- if (game == null) {
- ::ClientMessage("Can't make a move - not in a game.", player, 255, 255, 255);
- } else {
- game.MakeTurn(player, cell);
- }
- }
- function ProcessBindUp(player, bindId) {
- local keyId = -1;
- foreach (i, id in binds) {
- if (bindId == id) {
- MoveHandler(player, i);
- return;
- }
- }
- }
- function ProcessTimeouts() {
- foreach (i, game in games) {
- if (game != null) {
- if (!game.Process()) {
- games[i] = null;
- }
- }
- }
- }
- }
- class TTTMain {
- challengeManager = null;
- gameManager = null;
- constructor() {
- challengeManager = TTTChallengeManager(this);
- gameManager = TTTGameManager(this);
- }
- static function IsValidNumArg(argument) {
- return argument && ::IsNum(argument);
- }
- function GetPlayerFromArg(from, arguments) {
- if (!IsValidNumArg(arguments)) {
- ::ClientMessage("Invalid argument.", from, 255, 255, 255);
- return null;
- }
- local player = FindPlayer(arguments.tointeger());
- if (player == null) {
- ::ClientMessage("No such player.", from, 255, 255, 255);
- return null;
- }
- return player;
- }
- function ProcessCommand(player, cmd, arguments) {
- if (cmd == "challenge") {
- local challengee = GetPlayerFromArg(player, arguments);
- if (challengee != null) {
- challengeManager.MakeChallenge(player, challengee);
- }
- } else if (cmd == "withdraw") {
- challengeManager.WithdrawChallenge(player);
- } else if (cmd == "accept") {
- local challenger = GetPlayerFromArg(player, arguments);
- if (challenger != null) {
- challengeManager.AcceptChallenge(player, challenger);
- }
- } else if (cmd == "bet") {
- local args = split(arguments, " ");
- if (args.len() < 3 || !IsValidNumArg(args[0]) || (args[1] != "A" && args[1] != "B") || !IsValidNumArg(args[2])) {
- ::ClientMessage("Invalid format. Use: /bet <game> <A/B> <sum>", player, 255, 255, 255);
- return null;
- }
- local game = args[0].tointeger();
- local side = (args[1] == "A") ? 0 : 1;
- local sum = args[2].tointeger();
- gameManager.MakeBet(player, game, side, sum);
- } else if (cmd == "help") {
- ::ClientMessage("To challenge someone to a tic-tac-toe game, use the command /challenge <playerid>.", player, 255, 255, 255);
- ::ClientMessage("Once challenged, a player can accept a challenge via /accept <challengerid>.", player, 255, 255, 255);
- ::ClientMessage("After that bets can be made via /bet <gameid> <A/B> <sum>.", player, 255, 255, 255);
- ::ClientMessage("You can withdraw a challenge using /withdraw command.", player, 255, 255, 255);
- ::ClientMessage("To make a move in the tic-tac-toe game, use the number keys on your keyboard.", player, 255, 255, 255);
- ::ClientMessage("The numbers on the cells of the tic-tac-toe board show which key to press.", player, 255, 255, 255);
- } else {
- return 0;
- }
- return 1;
- }
- function ProcessBindUp(player, bindId) {
- gameManager.ProcessBindUp(player, bindId);
- }
- function CheckTimeouts() {
- challengeManager.RemoveExpiredChallenges();
- gameManager.ProcessTimeouts();
- }
- function PlayerQuit(player, reason) {
- challengeManager.PlayerQuit(player);
- gameManager.PlayerQuit(player);
- }
- }
- tttMain <- TTTMain();
- tickCount <- 0;
- function giveEveryoneSomeMoney() {
- local maxPlayers = ::GetMaxPlayers();
- for(local i = 0; i < maxPlayers; i++) {
- local player = ::FindPlayer(i);
- if (player != null) {
- player.GiveMoney(10);
- }
- }
- }
- function perSecond() {
- tttMain.CheckTimeouts();
- if (tickCount++ % 5 == 0) {
- giveEveryoneSomeMoney();
- }
- }
- function onScriptLoad() {
- ::NewTimer("perSecond", 1000, 0);
- }
- function onPlayerJoin(player) {
- ::ClientMessage("Welcome to the tic-tac-toe world! Type /help for instructions.", player, 255, 255, 255);
- }
- function onPlayerPart(player, reason) {
- tttMain.PlayerQuit(player, reason);
- }
- function onPlayerCommand(player, cmd, arguments) {
- return tttMain.ProcessCommand(player, cmd, arguments);
- }
- function onKeyDown(player, bindId) {
- tttMain.ProcessBindUp(player, bindId);
- }
Advertisement
Add Comment
Please, Sign In to add comment