diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..adff644 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Listen for XDebug", + "type": "php", + "request": "launch", + "port": 9000 + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 9000 + } + ] +} \ No newline at end of file diff --git a/base.db b/base.db new file mode 100644 index 0000000..9157306 Binary files /dev/null and b/base.db differ diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..c0ec069 --- /dev/null +++ b/config.yml @@ -0,0 +1,33 @@ +--- +# _ _ _ _ _ _____ _ +# | | | | (_) | | /\ | | / ____| | | +# | |__| | _ __| | ___ / \ _ __ __| | | (___ ___ ___ | | __ +# | __ | | | / _` | / _ \ / /\ \ | '_ \ / _` | \___ \ / _ \ / _ \ | |/ / +# | | | | | | | (_| | | __/ / ____ \ | | | | | (_| | ____) | | __/ | __/ | < +# |_| |_| |_| \__,_| \___| /_/ \_\ |_| |_| \__,_| |_____/ \___| \___| |_|\_\ +# +# +# The original minigame, free, and better than ever ! +# @author Ad5001 +# @link ad5001.eu + +# Welcome to HideAndSeek config. + +# Parameters below are defaul parameters when not configured per world. +# You can change them per world using in game commands +# Maximum amount of players per game. The game automaticly starts after 75% of the players have joined +Max players: 16 + +# Seconds to wait between the time 75% of the maximum players have joined and the games starts. +Waiting time: 60 + +# Minutes seekers will have to find to find all the hiders before they win. +Seeking time: 3 + +# Percentage of the players that will be seekers. +Seekers percentage: 10 + +# Parameters below are set for every game and are not changable by commands +# The world players should be tped to when the game ends. +Lobby world: world +... \ No newline at end of file diff --git a/src/Ad5001/HideAndSeek/DataBase.php b/src/Ad5001/HideAndSeek/DataBase.php index 2e16dc9..cb63077 100644 --- a/src/Ad5001/HideAndSeek/DataBase.php +++ b/src/Ad5001/HideAndSeek/DataBase.php @@ -87,7 +87,7 @@ class DataBase extends SQLite3 { @return SQLite3Result|bool */ public function query($qry) { - Server::getInstance()->getPluginManager()->getPlugin("HideAndSeek")->getLogger()->debug($qry); + // Server::getInstance()->getPluginManager()->getPlugin("HideAndSeek")->getLogger()->debug($qry); $res = parent::query($qry); if($res instanceof \SQLite3Result) self::setNumRows($res); return $res; diff --git a/src/Ad5001/HideAndSeek/Game.php b/src/Ad5001/HideAndSeek/Game.php index 7b1d069..a7f2d2e 100644 --- a/src/Ad5001/HideAndSeek/Game.php +++ b/src/Ad5001/HideAndSeek/Game.php @@ -25,11 +25,11 @@ use Ad5001\HideAndSeek\GameManager; class Game extends PluginTask /* Allows easy game running */ implements Listener { - const STEP_WAIT = 0; - const STEP_START = 1; - const STEP_HIDE = 2; - const STEP_SEEK = 3; - const STEP_WIN = 4; + const STEP_WAIT = "WAITING"; + const STEP_START = "STARTING"; + const STEP_HIDE = "HIDING"; + const STEP_SEEK = "SEEKING"; + const STEP_WIN = "RESTARTING"; const NO_WIN = 0; const WIN_SEEKERS = 1; @@ -80,6 +80,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener public function onRun($tick) { switch($this->step) { case self::STEP_WAIT: + // $this->getMain()->getLogger()->debug("Running game " . $this->getName() . " at wait step"); if(count($this->getPlayers()) >= round($this->getMaxPlayers() * 0.75)) { $this->stepTick = $tick; $this->step = self::STEP_START; @@ -89,6 +90,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener } break; case self::STEP_START: + // $this->getMain()->getLogger()->debug("Running game " . $this->getName() . " at start step"); $tickWaited = $tick - $this->stepTick; if($tickWaited % 20 == 0) { foreach(array_merge($this->getPlayers(), $this->getSpectators()) as $p) { @@ -100,7 +102,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener foreach(array_merge($this->getPlayers(), $this->getSpectators()) as $p) { $p->sendMessage(Main::PREFIX . "§aGame started ! There is $this->seekersCount seekers and $this->hidersLeft hiders."); if($p->HideAndSeekRole == self::ROLE_SEEK) { - $p->teleport($this->getSeekerSpawn()); + $p->teleport($this->getSeekersSpawn()); } elseif($p->HideAndSeekRole == self::ROLE_HIDE) { $p->teleport($this->getSpawn()); $p->sendPopup("§lHider: You have 1 minute to hide yourself so seekers won't find you ! Don't get caught for " . $this->getSeekTime() . " minutes to win !"); @@ -109,6 +111,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener } break; case self::STEP_HIDE: + // $this->getMain()->getLogger()->debug("Running game " . $this->getName() . " at hide step"); $tickWaited = $tick - $this->stepTick; if($tickWaited >= 20*60) { // One minute has past ! $this->step = self::STEP_SEEK; @@ -117,14 +120,15 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener $p->sendMessage(Main::PREFIX . "§aSeekers released !"); if($p->HideAndSeekRole == self::ROLE_SEEK) { $p->teleport($this->getSpawn()); - $p->sendPopup("§lSeeker: Seek the hiders ! Catch them all to win in " . $this->getSeekTime() . " minutes to win !"); + $p->sendMessage("§lSeeker: Seek the hiders ! Catch them all to win in " . $this->getSeekTime() . " minutes to win !"); } } } break; case self::STEP_SEEK: + // $this->getMain()->getLogger()->debug("Running game " . $this->getName() . " at seek step"); $tickWaited = $tick - $this->stepTick; - if($tickWaited % 20*60 == 0) { + if($tickWaited % (20*60) == 0) { foreach(array_merge($this->getPlayers(), $this->getSpectators()) as $p) { $p->sendMessage(Main::PREFIX . "§aGame ends in " . ($this->getSeekTime() - ($tickWaited / 20 / 60)) . " minutes."); } @@ -135,6 +139,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener } break; case self::STEP_WIN: + // $this->getMain()->getLogger()->debug("Running game " . $this->getName() . " at win step"); foreach(array_merge($this->getPlayers(), $this->getSpectators()) as $p) { if($this->win == self::WIN_SEEKERS) { $p->sendMessage(Main::PREFIX . "§aThe last hider got caught ! Seekers won !"); @@ -148,6 +153,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener $p->teleport($this->getMain()->getLobbyWorld()->getSafeSpawn()); $p->setGamemode($this->getMain()->getServer()->getDefaultGamemode()); } + $this->players = []; $this->step = self::STEP_WAIT; break; } @@ -190,7 +196,8 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener public function start() { $this->stepTick = $this->getMain()->getServer()->getTick(); $count = count($this->players); - $this->seekersCount = round($count * ($this->getSeekersPercentage() / 100), 0, PHP_ROUND_HALF_UP); // Minimum $this->getSeekersPercentage() percents of the players (inimum because if there are less than $this->getSeekersPercentage(), then there could be no seeker) + $this->step = self::STEP_HIDE; + $this->seekersCount = ceil($count * ($this->getSeekersPercentage() / 100)); // Minimum $this->getSeekersPercentage() percents of the players (inimum because if there are less than $this->getSeekersPercentage(), then there could be no seeker) $this->hidersLeft = count($this->players) - $this->seekersCount; shuffle($this->players); @@ -223,7 +230,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener Returns seekers spawn (waiting hiders to hide + startup) @return \pocketmine\math\Vector3 */ - public function getSeekerSpawn() : Vector3 { + public function getSeekersSpawn() : Vector3 { $data = $this->getMain()->getDatabase()->get("seekerspawn", ["table" => "Games", "name" => $this->getName()])->fetchArray()[0]; $data = explode(",", $data); if(!isset($data[1])) return $this->getLevel()->getSafeSpawn(); @@ -322,6 +329,22 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener public function getSeekersPercentage() : int { return (int) $this->getMain()->getDatabase()->get("seekers_percentage", ["table" => "Games", "name" => $this->getName()])->fetchArray()[0]; } + + /* + Returns the current number of seekers + @return int + */ + public function getSeekersCount() : int { + return $this->seekersCount; + } + + /* + Returns the current number of seekers + @return int + */ + public function getHidersLeft() : int { + return $this->hidersLeft; + } // SET @@ -338,7 +361,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener Sets the seeker spawn. Check get for more details. @param $v3 Vector3 */ - public function setSeekerSpawn(Vector3 $v3) { + public function setSeekersSpawn(Vector3 $v3) { $str = $v3->x . "," . $v3->y . "," . $v3->z; return $this->getMain()->getDatabase()->set("seekerspawn", $str, ["table" => "Games", "name" => $this->getName()])->fetchArray()[0]; } @@ -394,14 +417,13 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener $this->getMain()->getServer()->getPluginManager()->getPlugin("SpectatorPlus")->isSpectator($player))) { // Support for spectator Plus $this->spectators[$player->getName()] = $player; $player->HideAndSeekRole = self::ROLE_SPECTATE; - } elseif($this->step == self::STEP_WAIT || $this->step == self::STEP_START) { + } elseif($this->step == self::STEP_WAIT || $this->step == self::STEP_START) {$player->hideAndSeekGame = $this; // API inside player's class (easilier to get data) - $this->sendMessage("§a" . $player->getName() . " joined (" . count($this->players) . "/" . $this->getMaxPlayers() . "). " . (count($this->players) - round($this->getMaxPlayers() * 0.75)) . "players left before starting"); - $player->hideAndSeekGame = $this; $player->HideAndSeekRole = self::ROLE_WAIT; $player->playsHideAndSeek = true; $this->players[$player->getName()] = $player; $player->setGamemode(2); // Set it to adventure so player cannot break blocks. + $this->sendMessage("§a" . $player->getName() . " joined (" . count($this->players) . "/" . $this->getMaxPlayers() . "). " . (count($this->players) - round($this->getMaxPlayers() * 0.75)) . "players left before starting"); } else { $this->spectators[$player->getName()] = $player; $player->HideAndSeekRole = self::ROLE_SPECTATE; @@ -420,6 +442,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener $this->sendMessage($player->getName() . " left the game. $this->seekersCount seekers left."); unset($this->players[$player->getName()]); unset($player->hideAndSeekGame); + unset($player->HideAndSeekRole); unset($player->playsHideAndSeek); if($this->seekersCount == 0) { $this->step = self::STEP_WIN; @@ -431,6 +454,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener $this->sendMessage($player->getName() . " left the game. $this->hidersLeft hiders left."); unset($this->players[$player->getName()]); unset($player->hideAndSeekGame); + unset($player->HideAndSeekRole); unset($player->playsHideAndSeek); if($this->hidersLeft == 0) { $this->step = self::STEP_WIN; @@ -484,13 +508,25 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener $this->unregisterPlayer($event->getEntity()); } } + + /* + Checks when a seeker moves when he shouldn't. + @param $event \pocketmine\event\player\PlayerMoveEvent + */ + public function onPlayerMove(\pocketmine\event\player\PlayerMoveEvent $event) { + if($event->getPlayer()->getLevel()->getName() == $this->getName()) { + if($event->getPlayer()->HideAndSeekRole == self::ROLE_SEEK && $this->step == self::STEP_HIDE) { + $event->setCancelled(); + } + } + } /* Checks when a block breaks to prevent it. @param $event \pocketmine\event\block\BlockBreakEvent */ public function onBlockBreak(\pocketmine\event\block\BlockBreakEvent $event) { - if($event->getBlock()->getLevel()->getLevel() == $this->getName()) { + if($event->getBlock()->getLevel()->getName() == $this->getName()) { $event->setCancelled(); } } @@ -500,7 +536,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener @param $event \pocketmine\event\block\BlockPlaceEvent */ public function onBlockPlace(\pocketmine\event\block\BlockPlaceEvent $event) { - if($event->getBlock()->getLevel()->getLevel() == $this->getName()) { + if($event->getBlock()->getLevel()->getName() == $this->getName()) { $event->setCancelled(); } } @@ -511,7 +547,8 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener @param $event \pocketmine\event\entity\EntityDamageEvent */ public function onEntityDamage(\pocketmine\event\entity\EntityDamageEvent $event) { - if($event->getEntity()->getLevel() == $this->getName()) { + if($event->getEntity()->getLevel()->getName() == $this->getName()) { + $this->getMain()->getLogger()->debug("Cancelling game hit: " . $this->getName()); if($event instanceof \pocketmine\event\entity\EntityDamageByEntityEvent && $event->getEntity() instanceof Player && $event->getDamager() instanceof Player @@ -530,7 +567,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener @param $event \pocketmine\event\player\PlayerDeathEvent */ public function onPlayerDeath(\pocketmine\event\player\PlayerDeathEvent $event) { - if($event->getPlayer()->getLevel() == $this->getName()) { + if($event->getPlayer()->getLevel()->getName() == $this->getName()) { $event->setCancelled(); } } @@ -542,7 +579,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener @param $event \pocketmine\event\player\PlayerJoinEvent */ public function onPlayerJoin(\pocketmine\event\player\PlayerJoinEvent $event) { - if($event->getPlayer()->getLevel() == $this->getName()) { + if($event->getPlayer()->getLevel()->getName() == $this->getName()) { $this->registerPlayer($event->getPlayer()); } } @@ -552,7 +589,7 @@ class Game extends PluginTask /* Allows easy game running */ implements Listener @param $event \pocketmine\event\player\PlayerQuitEvent */ public function onPlayerQuit(\pocketmine\event\player\PlayerQuitEvent $event) { - if($event->getPlayer()->getLevel() == $this->getName()) { + if($event->getPlayer()->getLevel()->getName() == $this->getName()) { $this->unregisterPlayer($event->getPlayer()); } } diff --git a/src/Ad5001/HideAndSeek/GameManager.php b/src/Ad5001/HideAndSeek/GameManager.php index c77342e..d6ed066 100644 --- a/src/Ad5001/HideAndSeek/GameManager.php +++ b/src/Ad5001/HideAndSeek/GameManager.php @@ -144,6 +144,8 @@ class GameManager { if(is_null($row["name"])) return; if(is_null($this->gamesName[$row["name"]]) && ($lvl = $this->getMain()->getServer()->getLevelByName($row["name"])) !== null) { // Game doesn't exists && level is loaded $this->games[$row["Id"]] = new Game($lvl); + $this->getMain()->getServer()->getPluginManager()->registerEvents($this->games[$row["Id"]] ,$this->getMain()); + $this->getMain()->getServer()->getScheduler()->scheduleRepeatingTask($this->games[$row["Id"]], 1); $this->gamesNames[$row["name"]] = $row["Id"]; $this->getMain()->getLogger()->notice("Succefully registered game level {$row['name']}."); } diff --git a/src/Ad5001/HideAndSeek/Main.php b/src/Ad5001/HideAndSeek/Main.php index 79ce338..5a7099a 100644 --- a/src/Ad5001/HideAndSeek/Main.php +++ b/src/Ad5001/HideAndSeek/Main.php @@ -17,14 +17,20 @@ use pocketmine\command\CommandSender; use pocketmine\command\Command; use pocketmine\event\Listener; use pocketmine\plugin\PluginBase; +use pocketmine\nbt\tag\ListTag; +use pocketmine\nbt\tag\StringTag; use pocketmine\Server; use pocketmine\Player; + +use Ad5001\HideAndSeek\tasks\SignsTask; + class Main extends PluginBase implements Listener { const PREFIX = "§a§l§o[§r§l§2Hide§eAnd§6Seek§o§a] §r§f"; protected $db; protected $gamemanager; + protected $setsignsession = []; /* @@ -52,6 +58,7 @@ A } $this->gamemanager = new GameManager(); + //$this->getServer()->getScheduler()->scheduleRepeatingTask(new SignsTask($this), 10); } @@ -110,8 +117,8 @@ A case "setseekerspercentage": case "ssp": if(!is_null($game)) { - if(isset($args[1]) && is_int($args[1]) && $args[1] > 0 && $args[1] < 100) { - $game->setSeekersPercentage($args[1]); + if(isset($args[1]) && (int) $args[1] > 0 && (int) $args[1] < 100) { + $game->setSeekersPercentage((int) $args[1]); $sender->sendMessage(self::PREFIX . "§cSuccefully set seekers percentage of hide and seek game in level {$sender->getLevel()->getName()} to {$args[1]}."); } else { $sender->sendMessage("§cUsage: /hideandseek setseekerspercentage "); @@ -179,12 +186,12 @@ A if(count($game->getPlayers()) > 1) { $game->start(); foreach(array_merge($game->getPlayers(), $game->getSpectators()) as $p) { - $p->sendMessage(Main::PREFIX . "§aGame started ! There is $this->seekersCount seekers and $this->hidersLeft hiders."); - if($p->HideAndSeekRole == self::ROLE_SEEK) { - $p->teleport($game->getSeekerSpawn()); - } elseif($p->HideAndSeekRole == self::ROLE_HIDE) { + $p->sendMessage(Main::PREFIX . "§aGame started ! There is {$game->getSeekersCount()} seekers and {$game->getHidersLeft()} hiders."); + if($p->HideAndSeekRole == Game::ROLE_SEEK) { + $p->teleport($game->getSeekersSpawn()); + } elseif($p->HideAndSeekRole == Game::ROLE_HIDE) { $p->teleport($game->getSpawn()); - $p->sendPopup("§lHider: You have 1 minute to hide yourself so seekers won't find you ! Don't get caught for " . $this->getSeekTime() . " minutes to win !"); + $p->sendMessage("§lHider: You have 1 minute to hide yourself so seekers won't find you ! Don't get caught for " . $game->getSeekTime() . " minutes to win !"); } } } else { @@ -203,6 +210,21 @@ A } else { $sender->sendMessage(self::PREFIX . "§cYou're not in an hide and seek game world."); } + break; + case "setsign": + if(!isset($args[1])) { + $sender->sendMessage(self::PREFIX . "§cUsage: /hideandseek setsign "); + return true; + } + $game = $this->getGameManager()->getGameByName($args[1]); + if($game == null) { + $sender->sendMessage(self::PREFIX . "§cGame level $args[1] not found."); + return true; + } + $this->setsignsession[$sender->getName()] = $args[1]; + $sender->sendMessage(self::PREFIX . "§aTap a sign to create the teleportation sign to game $args[1]."); + return true; + break; default: $sender->sendMessage(str_ireplace(PHP_EOL, PHP_EOL . self::PREFIX,self::PREFIX. "§cSub-command {$args[0]} not found ! Possible subcommands: @@ -310,4 +332,29 @@ Please note that all those subcommands are relative to the world where you execu public function onLevelLoad(\pocketmine\event\level\LevelLoadEvent $event) { $this->getGameManager()->refreshRegisterGames($this->getDatabase()); } -} \ No newline at end of file + + /* + Checks when a player interacts. Used to set signs and tp to the world that the sign reports to. + @param $event \pocketmine\event\player\PlayerInteractEvent + */ + public function onInteract(\pocketmine\event\player\PlayerInteractEvent $event) { + $t = $event->getBlock()->getLevel()->getTile($event->getBlock()); + if($t instanceof \pocketmine\tile\Sign) { + if(isset($this->setsignsession[$event->getPlayer()->getName()])) { + $game = $this->getGameManager()->getGameByName($this->setsignsession[$event->getPlayer()->getName()]); + $name = $this->setsignsession[$event->getPlayer()->getName()]; + unset($this->setsignsession[$event->getPlayer()->getName()]); + if(!($game instanceof Game)) { + $event->getPlayer()->sendMessage(self::PREFIX . "§cSelected game ($name) doesn't exists anymore. Did you deleted it ?"); + return true; + } + safe_var_dump($t); + $t->namedtag->hideAndSeekSignData = new ListTag("hideAndSeekSignData", [ + "levelName" => new StringTag("levelName", $game->getName()) + ]); + $t->namedtag->hideAndSeekSignData->levelName = new StringTag("levelName", $game->getName()); + $event->getPlayer()->sendMessage(self::PREFIX."Succefully set this sign to point to world $name."); + } + } + } +} diff --git a/src/Ad5001/HideAndSeek/tasks/SignsTasks.php b/src/Ad5001/HideAndSeek/tasks/SignsTask.php similarity index 62% rename from src/Ad5001/HideAndSeek/tasks/SignsTasks.php rename to src/Ad5001/HideAndSeek/tasks/SignsTask.php index 900029a..3a90064 100644 --- a/src/Ad5001/HideAndSeek/tasks/SignsTasks.php +++ b/src/Ad5001/HideAndSeek/tasks/SignsTask.php @@ -21,10 +21,11 @@ use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\StringTag; use Ad5001\HideAndSeek\Main; +use Ad5001\HideAndSeek\Game; -class Task1 extends PluginTask { +class SignsTask extends PluginTask { public function __construct(Main $main) { @@ -40,11 +41,16 @@ class Task1 extends PluginTask { if($t instanceof Sign) { if(isset($t->namedtag->hideAndSeekSignData)) { if(!isset($t->namedtag->hideAndSeekSignData->model)) { - $t->namedtag->hideAndSeekSignData->model = new ListTag("model", [ - 1 => $t->namedtag->Text1, - 2 => $t->namedtag->Text2, - 3 => $t->namedtag->Text3, - 4 => $t->namedtag->Text4 + $this->getOwner()->getLogger()->debug("SettingModel l44"); + $t->namedtag->hideAndSeekSignData->setValue([ + "model" => new ListTag("model", [ + 1 => $t->namedtag->Text1, + 2 => $t->namedtag->Text2, + 3 => $t->namedtag->Text3, + 4 => $t->namedtag->Text4 + ]), + "levelName" => $t->namedtag->hideAndSeekSignData->level ?? $t->namedtag->hideAndSeekSignData->levelName, + "level" => $t->namedtag->hideAndSeekSignData->level ?? $t->namedtag->hideAndSeekSignData->levelName // Sometimes, there is no way to know why things like that works... ]); } $line1 = $this->parse((string) $t->namedtag->hideAndSeekSignData->model[1], $t); @@ -66,11 +72,14 @@ class Task1 extends PluginTask { @return string */ public function parse(string $string, Sign $sign) : string { - $game = $this->main->getGameManager()->getGameByName($sign->namedtag->hideAndSeekSignData->level); - $str = str_ireplace("{world}", $sign->namedtag->hideAndSeekSignData->level); - $str = str_ireplace("{maxp}", $game->getMaxPlayers()); - $str = str_ireplace("{pls}", count($game->getPlayers())); - $str = str_ireplace("{world}", $sign->namedtag->hideAndSeekSignData->level); + safe_var_dump($sign->namedtag->hideAndSeekSignData->getValue()); + $game = $this->main->getGameManager()->getGameByName($sign->namedtag->hideAndSeekSignData->levelName ?? $sign->namedtag->hideAndSeekSignData->level); + $str = str_ireplace("{world}", $sign->namedtag->hideAndSeekSignData->offsetGet("levelName"), $string); + $str = str_ireplace("{maxp}", $game->getMaxPlayers(), $str); + $str = str_ireplace("{pls}", count($game->getPlayers()), $str); + $str = str_ireplace("{step}", $game->step, $str); + $str = str_ireplace("{playing}", ($game->step == Game::STEP_WAIT || $game->step == Game::STEP_START) ? "Waiting" : "Starting", $str); + return $str; }