diff --git a/README.md b/README.md index b062293..a1f322c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,23 @@ -# [WIP] KitUI +# KitUI Select kits from a form! Just do /kit and get kits info and select it if available! For MCBE! PMMP support only! +(No Plugin library or Virion required! Easy as pie!) -## Holdup -Star, watch this repo if you're interested and want me to work on this! +## Usage +- /kit - List of kits fetched from kits.yml on a form! + +## Features +- Easy config +- Enchantments +- Effects +- Commands +- Cooldown +- Custom Names for items +- Economy +- Unlimited Kits + +## About +- Made by @Infernus101 +- Github - http://github.com/Infernus101 +- Twitter - http://twitter.com/Infernus101 +- Email - infernusmcpe@gmail.com +- Server - Fallentech.tk 19132 | Countertech.tk 19132 diff --git a/kit.png b/kit.png new file mode 100644 index 0000000..0a730fd Binary files /dev/null and b/kit.png differ diff --git a/plugin.yml b/plugin.yml new file mode 100644 index 0000000..722b210 --- /dev/null +++ b/plugin.yml @@ -0,0 +1,12 @@ +name: KitUI +author: Infernus101 +api: [3.0.0-ALPHA1, 3.0.0-ALPHA2, 3.0.0-ALPHA3, 3.0.0-ALPHA4, 3.0.0-ALPHA5, 3.0.0-ALPHA6, 3.0.0-ALPHA7, 3.0.0-ALPHA8, 3.0.0-ALPHA9] +main: Infernus101\KitUI\Main +version: 1.0.0 +permission: # kit permissions - kit. + kit.command: + description: kit command! + default: true +commands: + kit: + description: Kit UI! \ No newline at end of file diff --git a/resources/config.yml b/resources/config.yml new file mode 100644 index 0000000..fbead2a --- /dev/null +++ b/resources/config.yml @@ -0,0 +1,6 @@ +--- +# Players can't get another kit till they die +one-kit-per-life: true +# Players can claim another kit if they relog(only if one-kit-per-life is true) +reset-on-logout: true +... diff --git a/resources/kits.yml b/resources/kits.yml new file mode 100644 index 0000000..4756ce0 --- /dev/null +++ b/resources/kits.yml @@ -0,0 +1,41 @@ +--- +testkit: + # Format: "id:damage:count:name:ench_name:ench_level" + + # If you want only custom name (no enchantments): "id:damage:count:name" + + # If you don't want enchantments or custom name: "id:damage:count" + + # If you want only enchantments (no custom name): "id:damage:count:DEFAULT:ench_name:ench_level" -- you have to put DEFAULT in the name field + + # If you want more than one enchantment just do: "id:damage:count:name:ench1_name:ench1_level:ench2_name:ench2_level" + # or "id:damage:count:DEFAULT:ench1_name:ench1_level:ench2_name:ench2_level" if you don't want a custom item name + + # Please note: You have to write numeric IDs + items: + - "260:0:10" + - "267:0:1:Sword Name:sharpness:3:knockback:1" + helmet: "302:0:1" + chestplate: "303:0:1:DEFAULT:protection:1" + leggings: "304:0:1:Leggings Name" + boots: "305:0:1" + + commands: # {player} will get replaced by player's name + - "tell {player} Kit claimed!" + + cooldown: + hours: 24 + minutes: 30 + + # Format: "name:time:amplifier" + # Time is in seconds + effects: + - "speed:120:2" + + # Add a cost for the kit. Compatible with EconomyAPI only + # Put 0 if you want the kit to be free + money: 50 + + # Info to show when selected a Kit in UI form + info: "This kit is too op XD Cost: 50$" +... \ No newline at end of file diff --git a/src/Infernus101/KitUI/Kit.php b/src/Infernus101/KitUI/Kit.php new file mode 100644 index 0000000..bea180d --- /dev/null +++ b/src/Infernus101/KitUI/Kit.php @@ -0,0 +1,159 @@ +pl = $pl; + $this->data = $data; + $this->name = $name; + $this->timer = $this->getTimerMinutes(); + if(file_exists($this->pl->getDataFolder()."timer/".strtolower($this->name).".sl")){ + $this->timers = unserialize(file_get_contents($this->pl->getDataFolder()."timer/".strtolower($this->name).".sl")); + } + if(isset($this->data["money"]) and $this->data["money"] != 0){ + $this->cost = (int) $this->data["money"]; + } + } + + public function getName() : string{ + return $this->name; + } + + public function isInventoryFull(Player $player){ + $full = true; + for($i = 0; $i < $player->getInventory()->getSize(); $i++){ + if($player->getInventory()->getItem($i)->getId() === 0){ + $full = false; + } + } + return $full; + } + + public function add(Player $player){ + $inv = $player->getInventory(); + $flag = false; + + isset($this->data["helmet"]) and $inv->setHelmet($this->loadItem(...explode(":", $this->data["helmet"]))); + isset($this->data["chestplate"]) and $inv->setChestplate($this->loadItem(...explode(":", $this->data["chestplate"]))); + isset($this->data["leggings"]) and $inv->setLeggings($this->loadItem(...explode(":", $this->data["leggings"]))); + isset($this->data["boots"]) and $inv->setBoots($this->loadItem(...explode(":", $this->data["boots"]))); + + foreach($this->data["items"] as $itemString){ + if(!$this->isInventoryFull($player)){ + $inv->setItem($inv->firstEmpty(), $i = $this->loadItem(...explode(":", $itemString))); + } + else{ + $flag = true; + } + } + + if($flag == true){ + $player->sendMessage($this->pl->language->getTranslation("inv-full")); + } + + if(isset($this->data["effects"])){ + foreach($this->data["effects"] as $effectString){ + $e = $this->loadEffect(...explode(":", $effectString)); + if($e !== null){ + $player->addEffect($e); + } + } + } + + if(isset($this->data["commands"]) and is_array($this->data["commands"])){ + foreach($this->data["commands"] as $cmd){ + $this->pl->getServer()->dispatchCommand(new ConsoleCommandSender(), str_replace("{player}", $player->getName(), $cmd)); + } + } + + if($this->timer){ + $this->timers[strtolower($player->getName())] = $this->timer; + } + + $this->pl->hasKit[strtolower($player->getName())] = $this; + $player->sendMessage($this->pl->language->getTranslation("sel-kit", ucfirst($this->name))); + + } + + public function loadItem(int $id = 0, int $damage = 0, int $count = 1, string $name = "default", ...$enchantments) : Item{ + $item = Item::get($id, $damage, $count); + if(strtolower($name) !== "default"){ + $item->setCustomName($name); + } + foreach($enchantments as $key => $name_level){ + if($key % 2 === 0){ //Name expected + $ench = Enchantment::getEnchantmentByName($name_level); + }else{ //Level expected + if(isset($ench) and $ench !== null){ + $item->addEnchantment($ench->setLevel($name_level)); + } + } + } + return $item; + } + + public function loadEffect(string $name = "INVALID", int $seconds = 60, int $amplifier = 1){ + $e = Effect::getEffectByName($name); + if($e !== null){ + return $e->setDuration($seconds * 20)->setAmbient($amplifier); + } + return null; + } + + public function getTimerMinutes() : int{ + $min = 0; + if(isset($this->data["cooldown"]["minutes"])){ + $min += (int) $this->data["cooldown"]["minutes"]; + } + if(isset($this->data["cooldown"]["hours"])){ + $min += (int) $this->data["cooldown"]["hours"] * 60; + } + return $min; + } + + public function getTimerLeft(Player $player) : string{ + if(($minutes = $this->timers[strtolower($player->getName())]) < 60){ + return $this->pl->language->getTranslation("timer-format1", $minutes); + } + if(($modulo = $minutes % 60) !== 0){ + return $this->pl->language->getTranslation("timer-format2", floor($minutes / 60), $modulo); + } + return $this->pl->language->getTranslation("timer-format3", $minutes / 60); + } + + public function processTimer(){ + foreach($this->timers as $player => $min){ + $this->timers[$player] -= 1; + if($this->timers[$player] <= 0){ + unset($this->timers[$player]); + } + } + } + + public function testPermission(Player $player) : bool{ + return $player->hasPermission("kit.".strtolower($this->name)); + } + + public function save(){ + if(count($this->timers) > 0){ + file_put_contents($this->pl->getDataFolder()."timer/".strtolower($this->name).".sl", serialize($this->timers)); + } + } + +} \ No newline at end of file diff --git a/src/Infernus101/KitUI/Main.php b/src/Infernus101/KitUI/Main.php new file mode 100644 index 0000000..ea844b5 --- /dev/null +++ b/src/Infernus101/KitUI/Main.php @@ -0,0 +1,111 @@ +getDataFolder()."timer/"); + $this->getServer()->getLogger()->notice("[KitUI] Enabled! - By Infernus101"); + $this->configFixer(); + $files = array("kits.yml","config.yml"); + foreach($files as $file){ + if(!file_exists($this->getDataFolder() . $file)){ + @mkdir($this->getDataFolder()); + file_put_contents($this->getDataFolder() . $file, $this->getResource($file)); + } + } + $this->kit = new Config($this->getDataFolder() . "kit.yml", Config::YAML); + $this->config = new Config($this->getDataFolder() . "config.yml", Config::YAML); + $this->language = new LangManager($this); + $this->getServer()->getPluginManager()->registerEvents(new PlayerEvents($this), $this); + $this->getServer()->getScheduler()->scheduleDelayedRepeatingTask(new CoolDownTask($this), 1200, 1200); + $allKits = yaml_parse_file($this->getDataFolder()."kits.yml"); + foreach($allKits as $name => $data){ + $this->kits[$name] = new Kit($this, $data, $name); + } + } + + public function onDisable(){ + foreach($this->kits as $kit){ + $kit->save(); + } + $this->getServer()->getLogger()->notice("[KitUI] Disabled! - By Infernus101"); + } + + public function onCommand(CommandSender $sender, Command $cmd, String $label, array $args): bool{ + if(!$sender instanceof Player){ + $sender->sendMessage(TextFormat::RED."> Command must be run ingame!"); + return true; + } + switch(strtolower($cmd->getName())){ + case "kit": + if(isset($args[0])){ + $sender->sendMessage(TextFormat::GREEN."About:\nKit UI by Infernus101! github.com/Infernus101/KitUI\n".TextFormat::AQUA."Servers - FallenTech.tk | CounterTech.tk 19132"); + return false; + } + $handler = new Handler(); + $packet = new ModalFormRequestPacket(); + $packet->formId = $handler->getWindowIdFor(Handler::KIT_MAIN_MENU); + $packet->formData = $handler->getWindowJson(Handler::KIT_MAIN_MENU, $this, $sender); + $sender->dataPacket($packet); + break; + } + return true; + } + + private function configFixer(){ + $this->saveResource("kits.yml"); + $allKits = yaml_parse_file($this->getDataFolder()."kits.yml"); + $this->fixConfig($allKits); + foreach($allKits as $name => $data){ + $this->kits[$name] = new Kit($this, $data, $name); + } + } + + private function fixConfig(&$config){ + foreach($config as $name => $kit){ + if(isset($kit["users"])){ + $users = array_map("strtolower", $kit["users"]); + $config[$name]["users"] = $users; + } + if(isset($kit["worlds"])){ + $worlds = array_map("strtolower", $kit["worlds"]); + $config[$name]["worlds"] = $worlds; + } + } + } + + public function getPlayerKit($player, $obj = false){ + if($player instanceof Player){ + $player = $player->getName(); + } + return isset($this->kitused[strtolower($player)]) ? ($obj ? $this->kitused[strtolower($player)] : $this->kitused[strtolower($player)]->getName()) : null; + } + + public function getKit(string $kit){ + $lower = array_change_key_case($this->kits, CASE_LOWER); + if(isset($lower[strtolower($kit)])){ + return $lower[strtolower($kit)]; + } + return null; + } + } diff --git a/src/Infernus101/KitUI/PlayerEvents.php b/src/Infernus101/KitUI/PlayerEvents.php new file mode 100644 index 0000000..6dcf75b --- /dev/null +++ b/src/Infernus101/KitUI/PlayerEvents.php @@ -0,0 +1,50 @@ +pl = $pg; + } + + public function onDeath(PlayerDeathEvent $event){ + if(isset($this->pl->kitused[strtolower($event->getEntity()->getName())])){ + unset($this->pl->kitused[strtolower($event->getEntity()->getName())]); + } + } + + public function onLogOut(PlayerQuitEvent $event){ + if($this->pl->config->get("reset-on-logout") and isset($this->pl->kitused[strtolower($event->getPlayer()->getName())])){ + unset($this->pl->kitused[strtolower($event->getPlayer()->getName())]); + } + } + + public function onDataPacket(DataPacketReceiveEvent $event){ + $packet = $event->getPacket(); + if($packet instanceof ModalFormResponsePacket) { + if(json_decode($packet->formData, true) === null) { + return; + } + $windowHandler = new Handler(); + $packet->formId = $windowHandler->getWindowIdFor($packet->formId); + if(!$windowHandler->isInRange($packet->formId)) { + return; + } + $window = $windowHandler->getWindow($packet->formId, $this->pl, $event->getPlayer()); + $window->handle($packet); + } + } +} \ No newline at end of file diff --git a/src/Infernus101/KitUI/UI/Handler.php b/src/Infernus101/KitUI/UI/Handler.php new file mode 100644 index 0000000..2ce034b --- /dev/null +++ b/src/Infernus101/KitUI/UI/Handler.php @@ -0,0 +1,47 @@ +getWindow($windowId, $loader, $player)->getJson(); + } + + public function getWindow(int $windowId, Main $loader, Player $player): Window { + if(!isset($this->types[$windowId])) { + throw new \OutOfBoundsException("Tried to get window of non-existing window ID."); + } + return new $this->types[$windowId]($loader, $player); + } + + public function isInRange(int $windowId): bool { + if(isset($this->types[$windowId]) || isset($this->types[$windowId + 3200])) { + return true; + } + return false; + } + + public function getWindowIdFor(int $windowId): int { + if($windowId >= 3200) { + return $windowId - 3200; + } + return 3200 + $windowId; + } +} \ No newline at end of file diff --git a/src/Infernus101/KitUI/UI/Window.php b/src/Infernus101/KitUI/UI/Window.php new file mode 100644 index 0000000..d2c7ec3 --- /dev/null +++ b/src/Infernus101/KitUI/UI/Window.php @@ -0,0 +1,64 @@ +pl = $pl; + $this->player = $player; + $this->process(); + } + + public function getJson(): string { + return json_encode($this->data); + } + + public function getLoader(): Loader { + return $this->pl; + } + + public function getPlayer(): Player { + return $this->player; + } + + public function navigate(int $menu, Player $player, Handler $windowHandler): void { + $packet = new ModalFormRequestPacket(); + $packet->formId = $windowHandler->getWindowIdFor($menu); + $packet->formData = $windowHandler->getWindowJson($menu, $this->pl, $player); + $player->dataPacket($packet); + } + + public function navigateKit(int $menu, Player $player, Handler $windowHandler, $kit): void { + self::$kit = $kit; + $packet = new ModalFormRequestPacket(); + $packet->formId = $windowHandler->getWindowIdFor($menu); + $packet->formData = $windowHandler->getWindowJson($menu, $this->pl, $player); + $player->dataPacket($packet); + } + + public function navigateError(int $menu, Player $player, Handler $windowHandler, $error): void { + self::$error = $error; + $packet = new ModalFormRequestPacket(); + $packet->formId = $windowHandler->getWindowIdFor($menu); + $packet->formData = $windowHandler->getWindowJson($menu, $this->pl, $player); + $player->dataPacket($packet); + } + + protected abstract function process(): void; + + public abstract function handle(ModalFormResponsePacket $packet): bool; +} diff --git a/src/Infernus101/KitUI/UI/windows/KitError.php b/src/Infernus101/KitUI/UI/windows/KitError.php new file mode 100644 index 0000000..c6cfa58 --- /dev/null +++ b/src/Infernus101/KitUI/UI/windows/KitError.php @@ -0,0 +1,40 @@ +pl->language->getTranslation("error-title"); + $this->data = [ + "type" => "modal", + "title" => $title, + "content" => parent::$error, + "button1" => "Go Back", + "button2" => "Exit" + ]; + } + + private function select($index){ + $windowHandler = new Handler(); + switch($index){ + case "true\n": + $this->navigate(Handler::KIT_MAIN_MENU, $this->player, $windowHandler); + break; + case "false\n": + break; + } + } + public function handle(ModalFormResponsePacket $packet): bool { + $index = $packet->formData; + $this->select($index); + return true; + } +} \ No newline at end of file diff --git a/src/Infernus101/KitUI/UI/windows/KitInfo.php b/src/Infernus101/KitUI/UI/windows/KitInfo.php new file mode 100644 index 0000000..2a38deb --- /dev/null +++ b/src/Infernus101/KitUI/UI/windows/KitInfo.php @@ -0,0 +1,84 @@ +pl->getKit(parent::$kit); + if(isset($kits->data["info"])) $info = $kits->data["info"]; + } + $title = $this->pl->language->getTranslation("select-option"); + $this->data = [ + "type" => "modal", + "title" => $title, + "content" => $info, + "button1" => "Yes", + "button2" => "No" + ]; + } + + private function select($index){ + $windowHandler = new Handler(); + switch($index){ + case "true\n": + if(parent::$kit == null){ + $error = "Wrong Session! Try again!"; + $this->navigateError(Handler::KIT_ERROR, $this->player, $windowHandler, $error); + break; + } + $kits = $this->pl->getKit(parent::$kit); + if($kits != null){ + $name = $kits->getName(); + }else{ + $error = "Kit not found! Try again!"; + $this->navigateError(Handler::KIT_ERROR, $this->player, $windowHandler, $error); + break; + } + if(!$kits->testPermission($this->player)){ + $error = $this->pl->language->getTranslation("noperm", $name); + $this->navigateError(Handler::KIT_ERROR, $this->player, $windowHandler, $error); + break; + } + if(isset($kits->timers[strtolower($this->player->getName())])){ + $left = $kits->getTimerLeft($this->player); + $error = $this->pl->language->getTranslation("timer1", $name) . "\n" . $this->pl->language->getTranslation("timer2", $left); + $this->navigateError(Handler::KIT_ERROR, $this->player, $windowHandler, $error); + break; + } + if(isset($kits->data["money"])){ + $money = $kits->data["money"]; + if(EconomyAPI::getInstance()->reduceMoney($this->player, $money) === EconomyAPI::RET_INVALID){ + $error = $this->pl->language->getTranslation("cant-afford", $name, $money); + $this->navigateError(Handler::KIT_ERROR, $this->player, $windowHandler, $error); + break; + } + } + if(($this->pl->config->get("one-kit-per-life")) and (isset($kits->pl->kitused[strtolower($this->player->getName())]))){ + $error = $this->pl->language->getTranslation("one-per-life"); + $this->navigateError(Handler::KIT_ERROR, $this->player, $windowHandler, $error); + break; + } + $kits->add($this->player); + $this->player->sendMessage($this->pl->language->getTranslation("selected-kit", $name)); + break; + case "false\n": + $this->navigate(Handler::KIT_MAIN_MENU, $this->player, $windowHandler); + break; + } + } + public function handle(ModalFormResponsePacket $packet): bool { + $index = $packet->formData; + $this->select($index); + return true; + } +} \ No newline at end of file diff --git a/src/Infernus101/KitUI/UI/windows/KitMainMenu.php b/src/Infernus101/KitUI/UI/windows/KitMainMenu.php new file mode 100644 index 0000000..572ba38 --- /dev/null +++ b/src/Infernus101/KitUI/UI/windows/KitMainMenu.php @@ -0,0 +1,38 @@ +pl->language->getTranslation("mainmenu-title"); + $content = $this->pl->language->getTranslation("mainmenu-content"); + $this->data = [ + "type" => "form", + "title" => $title, + "content" => $content, + "buttons" => [] + ]; + foreach($this->pl->kits as $name => $data){ + $name = ucfirst($name); + $this->data["buttons"][] = ["text" => "§f$name"]; + array_push(parent::$id, "$name"); + } + } + + public function handle(ModalFormResponsePacket $packet): bool { + $index = (int) $packet->formData + 1; + $windowHandler = new Handler(); + if(isset(parent::$id[$index])) $kit = parent::$id[$index]; + else $kit = null; + $this->navigateKit(Handler::KIT_INFO, $this->player, $windowHandler, $kit); + return true; + } +} diff --git a/src/Infernus101/KitUI/lang/LangManager.php b/src/Infernus101/KitUI/lang/LangManager.php new file mode 100644 index 0000000..8fb012d --- /dev/null +++ b/src/Infernus101/KitUI/lang/LangManager.php @@ -0,0 +1,55 @@ +pl = $pl; + $this->defaults = [ + "lang-version" => 0, + "error-title" => "Error:", + "mainmenu-title" => "Kits -", + "mainmenu-content" => "Select a kit for info -", + "select-option" => "Do you wanna select this kit, player?", + "selected-kit" => "Selected kit: {%0}", + "inv-full" => "> Some of the items were not added to your inventory since your inventory got full!", + "cant-afford" => "You cannot afford kit: {%0} Cost: {%1}", + "one-per-life" => "You can only get one kit per life", + "timer1" => "Kit {%0} is in cooldown at the moment", + "timer2" => "You will be able to get it in {%0}", + "noperm" => "You don't have the permission to use kit {%0}", + "timer-format1" => "{%0} minutes", + "timer-format2" => "{%0} hours and {%1} minutes", + "timer-format3" => "{%0} hours", + ]; + $this->data = new Config($this->pl->getDataFolder()."lang.properties", Config::PROPERTIES, $this->defaults); + if($this->data->get("lang-version") != self::LANG_VERSION){ + $this->pl->getLogger()->alert("Translation file is outdated. The old file has been renamed and a new one has been created"); + @rename($this->pl->getDataFolder()."lang.properties", $this->pl->getDataFolder()."lang.properties.old"); + $this->data = new Config($this->pl->getDataFolder()."lang.properties", Config::PROPERTIES, $this->defaults); + } + } + + public function getTranslation(string $dataKey, ...$args) : string{ + if(!isset($this->defaults[$dataKey])){ + $this->pl->getLogger()->error("Invalid datakey $dataKey passed to method LangManager::getTranslation()"); + return ""; + } + $str = $this->data->get($dataKey, $this->defaults[$dataKey]); + foreach($args as $key => $arg){ + $str = str_replace("{%".$key."}", $arg, $str); + } + return $str; + } + +} diff --git a/src/Infernus101/KitUI/tasks/CoolDownTask.php b/src/Infernus101/KitUI/tasks/CoolDownTask.php new file mode 100644 index 0000000..046f896 --- /dev/null +++ b/src/Infernus101/KitUI/tasks/CoolDownTask.php @@ -0,0 +1,23 @@ +plugin = $plugin; + } + + public function onRun(int $tick){ + foreach($this->plugin->kits as $kit){ + $kit->processTimer(); + } + } + +}