├── .gitignore ├── resources ├── players.yml ├── config.yml └── messages.yml ├── .gitattributes ├── plugin.yml ├── src └── TheClimbing │ └── RPGLike │ ├── ItemSets │ └── BaseSet.php │ ├── Items │ ├── EpicTierItem.php │ ├── RareTierItem.php │ ├── MythicTierItem.php │ ├── Armor │ │ ├── CustomDiamondHelmet.php │ │ └── BaseCustomArmor.php │ ├── UncommonTierItem.php │ ├── BaseAxe.php │ ├── BaseHoe.php │ ├── BaseShovel.php │ ├── BaseSword.php │ ├── BasePIckaxe.php │ ├── BaseArmor.php │ ├── ItemFactory.php │ ├── BaseItem.php │ └── BaseTierItem.php │ ├── Skills │ ├── ActiveSkill.php │ ├── AreaOfEffect.php │ ├── PassiveSkill.php │ ├── Fortress.php │ ├── Tank.php │ ├── DoubleStrike.php │ ├── Explosion.php │ ├── Coinflip.php │ ├── HealingAura.php │ └── BaseSkill.php │ ├── Tasks │ ├── HudTask.php │ ├── CooldownTask.php │ └── HealTask.php │ ├── Commands │ ├── LevelUpCommand.php │ ├── RPGCommand.php │ └── PartyCommand.php │ ├── Systems │ ├── PartySystem.php │ └── BaseParty.php │ ├── Utils.php │ ├── RPGLike.php │ ├── Traits │ └── BaseTrait.php │ ├── EventListener.php │ ├── Forms │ └── RPGForms.php │ └── Players │ └── RPGPlayer.php ├── .poggit.yml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* -------------------------------------------------------------------------------- /resources/players.yml: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: RPGLike 3 | version: 0.0.1 4 | main: TheClimbing\RPGLike\RPGLike 5 | api: 5.0.0 6 | ... 7 | -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/ItemSets/BaseSet.php: -------------------------------------------------------------------------------- 1 | init('',$bonus); 16 | } 17 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BaseHoe.php: -------------------------------------------------------------------------------- 1 | init('',$bonus); 16 | } 17 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BaseShovel.php: -------------------------------------------------------------------------------- 1 | init('', $bonus); 16 | } 17 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BaseSword.php: -------------------------------------------------------------------------------- 1 | init('', $bonus); 16 | } 17 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BasePIckaxe.php: -------------------------------------------------------------------------------- 1 | init('',$bonus); 16 | } 17 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Tasks/HudTask.php: -------------------------------------------------------------------------------- 1 | main = $main; 17 | } 18 | 19 | public function onRun() : void 20 | { 21 | $players = $this->main->getServer()->getOnlinePlayers(); 22 | foreach ($players as $player) { 23 | $player->sendPopup($this->main->getHUD($player)); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/Fortress.php: -------------------------------------------------------------------------------- 1 | setAbsorption($player->getAbsorption() * 1.2); 20 | } 21 | 22 | public function passiveEffect(mixed $mixed) 23 | { 24 | $this->setDefense($mixed); 25 | } 26 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/Tank.php: -------------------------------------------------------------------------------- 1 | getMaxHealth(); 20 | $player->setMaxHealth((int)($health * 1.15)); 21 | } 22 | 23 | public function passiveEffect(mixed $mixed) 24 | { 25 | $this->setPlayerHealth($mixed); 26 | } 27 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Tasks/CooldownTask.php: -------------------------------------------------------------------------------- 1 | player = $player; 18 | $this->skillName = $skillName; 19 | } 20 | 21 | public function onRun() : void 22 | { 23 | $this->player->getSkill($this->skillName)->removeCooldown(); 24 | $this->player->sendMessage($this->skillName . ' off cooldown'); 25 | } 26 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Tasks/HealTask.php: -------------------------------------------------------------------------------- 1 | source = $source; 16 | $this->target = $target; 17 | } 18 | 19 | public function onRun() : void 20 | { 21 | $this->source->removeCooldown(); 22 | $target = $this->target; 23 | $this->source->setPlayerEffect(function () use ($target) { 24 | $target->setHealth($target->getHealth() + 1); 25 | }); 26 | } 27 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Commands/LevelUpCommand.php: -------------------------------------------------------------------------------- 1 | getXpManager(); 22 | if (empty($args)) { 23 | $xpmanager->setXpLevel($xpmanager->getXpLevel() + 1); 24 | } else { 25 | $xpmanager->setXpLevel($args[0]); 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BaseArmor.php: -------------------------------------------------------------------------------- 1 | new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET) , 17 | 'diamond_boots' => new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET), 18 | 19 | ]; 20 | parent::__construct(new ItemIdentifier($item_id, 0), $name, new ArmorTypeInfo($defense_points,$max_durability,$armor_type)); 21 | $this->init($name, $bonus); 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/DoubleStrike.php: -------------------------------------------------------------------------------- 1 | getEntity(); 22 | if ($player instanceof RPGPlayer){ 23 | if (mt_rand(0, 99) < $this->skillConfig['levels'][$this->getSkillLevel()]['chance']) { 24 | $event->setAttackCooldown(0); 25 | $this->transmitProcMessage(); 26 | } 27 | } 28 | } 29 | 30 | public function passiveEffect(mixed $mixed) 31 | { 32 | $this->setPlayerAttackCD($mixed); 33 | } 34 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Systems/PartySystem.php: -------------------------------------------------------------------------------- 1 | playerInThisParty($playerName)){ 30 | return $party; 31 | } 32 | } 33 | return false; 34 | } 35 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/ItemFactory.php: -------------------------------------------------------------------------------- 1 | getCustomName()] = $item; 17 | } 18 | } 19 | public static function getItem(string $itemName) 20 | { 21 | if (array_key_exists($itemName, self::$items)) { 22 | return self::$items[$itemName]; 23 | } 24 | return false; 25 | } 26 | public static function createItems(array $tiers) 27 | { 28 | $item_arrays = Utils::getItems(); 29 | $items = []; 30 | foreach ($item_arrays as $key => $item) { 31 | if ($key == 'stone_sword') { 32 | new BaseSword(new ItemIdentifier($item[0], $item[1]), $item[2], $item[3], $item[4]); 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/Explosion.php: -------------------------------------------------------------------------------- 1 | isOnCooldown()) { 24 | $damager->sendMessage('Skill on cooldown: ' . $this->getRemainingCooldown('M:S') . ' left'); 25 | return; 26 | } 27 | $pos = $hit_entity->getPosition(); 28 | $explosion = new \pocketmine\world\Explosion($pos, 2 + $this->getSkillLevel()); 29 | $explosion->explodeB(); 30 | $this->setOnCooldown(); 31 | 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/Coinflip.php: -------------------------------------------------------------------------------- 1 | setCritChance($mixed); 25 | } 26 | 27 | public function setCritChance(EntityDamageByEntityEvent $event) 28 | { 29 | $damage = $event->getBaseDamage(); 30 | if (rand(0, 99) < $this->skillConfig['levels'][$this->getSkillLevel()]['chance']) { 31 | $event->setModifier($damage * 1.5, EntityDamageEvent::CAUSE_ENTITY_ATTACK); 32 | $player = $event->getDamager(); 33 | if ($player instanceof RPGPlayer){ 34 | $this->transmitProcMessage(); 35 | } 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/Armor/BaseCustomArmor.php: -------------------------------------------------------------------------------- 1 | item_tier = $item_tier; 24 | if (array_search($bonus[array_key_first($bonus)], $this->available_bonuses)){ 25 | $this->bonus = $bonus; 26 | }else{ 27 | RPGLike::getInstance()->getLogger()->alert('All available bonuses are: damage, health, defense, movement_speed, mining_speed, jump_power, mana'); 28 | $this->bonus = ['damage' => 1]; 29 | } 30 | } 31 | public function getItemBonus(): array 32 | { 33 | return $this->bonus; 34 | } 35 | public function setCustomLore(string $tier) 36 | { 37 | $lore = RPGLike::getInstance()->getTieredItems()[$tier][$this->getCustomName()]; 38 | } 39 | //todo 40 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BaseItem.php: -------------------------------------------------------------------------------- 1 | item_tier = 'temp'; 27 | if (array_search(array_key_first($bonus), $this->available_bonuses)) { 28 | $this->bonus = $bonus; 29 | } else { 30 | RPGLike::getInstance()->getLogger()->alert('All available bonuses are: damage, health, defense, movement_speed, mining_speed, jump_power, mana'); 31 | $this->bonus = ['damage' => 1]; 32 | } 33 | $this->setEnchantGlow(); 34 | } 35 | 36 | 37 | public function setEnchantGlow(){} 38 | 39 | public function getItemBonus(): array 40 | { 41 | return $this->bonus; 42 | } 43 | 44 | public function setCustomLore(string $tier) 45 | { 46 | $original_lore = $this->getLore(); 47 | } 48 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/HealingAura.php: -------------------------------------------------------------------------------- 1 | isOnCooldown()){ 20 | if ($this->getRange() > 0) { 21 | $world = $this->owner->getWorld(); 22 | $players = $this->getNearestEntities($this->owner->getPosition(), $this->getRange(), $world, $this->getMaximumEntitiesInRange()); 23 | if (!empty($players)) { 24 | foreach ($players as $key => $player) { 25 | if ($key == 0) { 26 | continue; 27 | } 28 | $this->passiveEffect($player); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | public function passiveEffect(mixed $mixed) 35 | { 36 | $this->setOnCooldown(); 37 | RPGLike::getInstance()->getScheduler()->scheduleDelayedTask(new HealTask($this, $mixed), 5); 38 | } 39 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Systems/BaseParty.php: -------------------------------------------------------------------------------- 1 | party_players[$party_owner->getName()] = $party_owner; 24 | for($i = 1;$i<$party_size;$i++) { 25 | $party_players[] = ''; 26 | } 27 | $this->party_owner = $party_owner->getName(); 28 | $this->partyName = $partyName; 29 | $party_owner->partyName = $partyName; 30 | } 31 | public function getPartyMembers(): array 32 | { 33 | return $this->party_players; 34 | } 35 | public function playerInThisParty(string $player): bool 36 | { 37 | return array_key_exists($player, $this->party_players); 38 | } 39 | public function removePlayer(string $playerName){ 40 | $this->party_players[$playerName]->sendMessage("You've left the party"); 41 | unset($this->party_players[$playerName]); 42 | } 43 | public function addPlayerInParty(RPGPlayer $player){ 44 | $this->party_players[$player->getName()] = $player; 45 | $player->partyName = $this->partyName; 46 | } 47 | public function isPartyOwner(string $playerName): bool 48 | { 49 | return $playerName == $this->party_owner; 50 | } 51 | //TODO make this use player classes instead of names 52 | //TODO store base parties in party system each party will have for key in the array the player that opened the party 53 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Items/BaseTierItem.php: -------------------------------------------------------------------------------- 1 | item_tier = $item_tier; 32 | if (array_search($bonus[array_key_first($bonus)], $this->available_bonuses)) { 33 | $this->bonus = $bonus; 34 | } else { 35 | RPGLike::getInstance()->getLogger()->alert('All available bonuses are: damage, health, defense, movement_speed, mining_speed, jump_power, mana'); 36 | $this->bonus = ['damage' => 1]; 37 | } 38 | $this->setEnchantGlow(); 39 | } 40 | 41 | public function setEnchantGlow(): void 42 | { 43 | $this->setNamedTag((new CompoundTag())->setTag(self::TAG_ENCH, new ListTag())); 44 | } 45 | 46 | public function getItemBonus(): array 47 | { 48 | return $this->bonus; 49 | } 50 | 51 | public function setCustomLore(string $tier) 52 | { 53 | $this->custom_lore = RPGLike::getInstance()->getTieredItems()[$tier][$this->getCustomName()]; 54 | $this->setLore($this->custom_lore); 55 | } 56 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Commands/RPGCommand.php: -------------------------------------------------------------------------------- 1 | loader = $rpg; 20 | $this->setDescription('Opens RPG Menu'); 21 | $this->setUsage('rpg stats|skills|upgrade or rpg help '); 22 | $this->setPermissionMessage('You dont have permission to use this command'); 23 | } 24 | 25 | public function execute(CommandSender $sender, string $commandLabel, array $args) 26 | { 27 | if ($sender instanceof RPGPlayer && $sender->hasPermission($this->getPermission())) { 28 | if (empty($args) || $args[0] == '' || $args[0] == ' ') { 29 | RPGForms::menuForm($sender); 30 | } else { 31 | $args = array_map('strtolower', $args); 32 | switch ($args[0]) { 33 | case "stats": 34 | RPGForms::statsForm($sender); 35 | break; 36 | case "skills": 37 | RPGForms::skillsHelpForm($sender); 38 | break; 39 | case "help": 40 | if (array_key_exists(1, $args)) { 41 | RPGForms::skillHelpForm($sender, $args[1]); 42 | } else { 43 | $sender->sendMessage($this->getUsage()); 44 | } 45 | break; 46 | case "upgrade": 47 | RPGForms::upgradeStatsForm($sender); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Commands/PartyCommand.php: -------------------------------------------------------------------------------- 1 | |||'); 17 | $this->source = $RPGLike; 18 | } 19 | 20 | public function execute(CommandSender $sender, string $commandLabel, array $args) 21 | { 22 | if (empty($args) || $args[0] == '' || $args[0] == ' ') { 23 | $sender->sendMessage($this->getUsage()); 24 | } else { 25 | if ($sender instanceof RPGPlayer){ 26 | $args = array_map('strtolower', $args); 27 | switch ($args[0]) { 28 | case 'accept': 29 | if ($sender->hasPartyInvite()){ 30 | PartySystem::getPlayerParty($sender->getPartyInvite())->addPlayerInParty($sender); 31 | } 32 | break; 33 | case 'deny': 34 | $sender->removePartyInvite(); 35 | break; 36 | case 'create': 37 | PartySystem::createParty($args[1], $sender, 4); 38 | break; 39 | default: 40 | $targetPlayer = $this->source->getServer()->getPlayerExact($args[0]); 41 | if ($targetPlayer instanceof RPGPlayer){ 42 | if (!$targetPlayer->getParty()){ 43 | if ($sender->getParty()){ 44 | $targetPlayer->sendPartyInvite($sender->getParty()); 45 | }else{ 46 | $sender->sendMessage('You need to be in a party to send invites.'); 47 | } 48 | }else{ 49 | $sender->sendMessage('Player already in a party'); 50 | } 51 | }else{ 52 | $sender->sendMessage('The player is offline or is already in party'); 53 | } 54 | } 55 | } 56 | 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /resources/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | keep-xp: true 3 | discovery: false 4 | Hud: 5 | on: true 6 | message: '{HEALTH}/{MAXHP} MS: {MOVEMENTSPEED}| DMG: {DAMAGE} Defense: {DEFENSE}' 7 | period: 3 8 | timezone: 'UTC' 9 | Skills: 10 | Tank: 11 | levels: 12 | 1: 13 | unlock: 14 | VIT: 10 15 | chance: 10 16 | 2: 17 | unlock: 18 | VIT: 20 19 | chance: 15 20 | 3: 21 | unlock: 22 | VIT: 30 23 | DEX: 15 24 | chance: 20 25 | range: 0 26 | max_entities_in_range: 0 27 | is_aoe: false 28 | cooldown: 0 29 | is_active: false 30 | Coinflip: 31 | levels: 32 | 1: 33 | unlock: 34 | STR: 10 35 | chance: 5 36 | 2: 37 | unlock: 38 | STR: 20 39 | DEX: 10 40 | chance: 10 41 | 3: 42 | unlock: 43 | STR: 35 44 | DEX: 20 45 | chance: 20 46 | range: 0 47 | max_entities_in_range: 0 48 | is_aoe: false 49 | cooldown: 0 50 | is_active: false 51 | DoubleStrike: 52 | levels: 53 | 1: 54 | unlock: 55 | DEX: 10 56 | chance: 10 57 | 2: 58 | unlock: 59 | DEX: 20 60 | STR: 10 61 | chance: 15 62 | 3: 63 | unlock: 64 | DEX: 35 65 | STR: 20 66 | chance: 30 67 | range: 0 68 | max_entities_in_range: 0 69 | is_aoe: false 70 | cooldown: 0 71 | is_active: false 72 | Fortress: 73 | levels: 74 | 1: 75 | unlock: 76 | DEF: 10 77 | chance: 10 78 | 2: 79 | unlock: 80 | DEF: 20 81 | VIT: 10 82 | chance: 20 83 | 3: 84 | unlock: 85 | DEF: 35 86 | VIT: 20 87 | chance: 30 88 | range: 0 89 | max_entities_in_range: 0 90 | is_aoe: false 91 | cooldown: 0 92 | is_active: false 93 | Explosion: 94 | levels: 95 | 1: 96 | unlock: 97 | STR: 20 98 | DEX: 10 99 | chance: 0 100 | 2: 101 | unlock: 102 | STR: 40 103 | DEX: 10 104 | chance: 0 105 | range: 0 106 | max_entities_in_range: 0 107 | is_aoe: false 108 | cooldown: 10 109 | is_active: true 110 | HealingAura: 111 | levels: 112 | 1: 113 | unlock: 114 | VIT: 20 115 | DEX: 10 116 | chance: 0 117 | 2: 118 | unlock: 119 | VIT: 40 120 | DEX: 15 121 | chance: 0 122 | range: 10 123 | max_entities_in_range: 0 124 | is_aoe: true 125 | cooldown: 0 126 | is_active: false 127 | Modifiers: 128 | STR: 0.15 129 | VIT: 0.175 130 | DEF: 0.1 131 | DEX: 0.005 132 | Traits: 133 | WoodCutter: 134 | blocks: [ "Oak Wood", "Spruce Wood", "Birch Wood", "Jungle Wood" ] 135 | action: "break" 136 | levels: 137 | 1: 138 | requirement: 50 139 | drop_chance: 15 140 | 2: 141 | requirement: 150 142 | drop_chance: 30 143 | Miner: 144 | blocks: [ "Stone", "Coal Ore", "Gravel", "Granite", "Andesite", "Cobblestone", "Iron Ore", "Sandstone" ] 145 | action: "break" 146 | levels: 147 | 1: 148 | requirement: 500 149 | drop_chance: 15 150 | 2: 151 | requirement: 1000 152 | drop_chance: 30 153 | PreciousMaterialsHunter: 154 | blocks: [ "Gold Ore", "Diamond Ore", "Lapis Lazuli Ore", "Redstone Ore", "Emerald Ore" ] 155 | action: "break" 156 | levels: 157 | 1: 158 | requirement: 100 159 | drop_chance: 15 160 | 2: 161 | requirement: 300 162 | drop_chance: 30 163 | DiamondHunter: 164 | blocks: [ "Diamond Ore" ] 165 | action: "break" 166 | levels: 167 | 1: 168 | requirement: 20 169 | drop_chance: 15 170 | 2: 171 | requirement: 50 172 | drop_chance: 30 173 | MonsterHunter: 174 | blocks: [ "Zombie", "Skeleton" ] 175 | action: 'kill' 176 | levels: 177 | 1: 178 | requirement: 20 179 | drop_chance: 20 180 | 2: 181 | requirement: 100 182 | drop_chance: 40 183 | ItemTiers: 184 | Uncommon: 185 | colour: green 186 | level_cap: 10 187 | items: 188 | 272: 189 | name: 'Training Sword' 190 | bonuses: 191 | damage: 1 192 | Rare: 193 | colour: blue 194 | level_cap: 20 195 | items: 196 | 267: 197 | name: 'Silver Sword' 198 | bonuses: 199 | damage: 1 200 | Epic: 201 | colour: purple 202 | level_cap: 30 203 | items: 204 | 276: 205 | name: 'Greatsword' 206 | bonuses: 207 | damage: 1 208 | Mythic: 209 | colour: red 210 | level_cap: 40 211 | items: 212 | 283: 213 | name: 'Special Sword' 214 | bonuses: 215 | damage: 1 216 | ... -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Utils.php: -------------------------------------------------------------------------------- 1 | $value) { 17 | $subject = str_replace($key, $value, $subject); 18 | } 19 | return $subject; 20 | } 21 | 22 | public static function parseArrayKeywords(array $keywords, array $messages): array 23 | { 24 | foreach ($messages as $key => $message) { 25 | if (is_array($message)) { 26 | foreach ($message as $key1 => $value) { 27 | $messages[$key][$key1] = self::parseKeywords($keywords, $value); 28 | } 29 | } else { 30 | $messages[$key] = self::parseKeywords($keywords, $message); 31 | } 32 | } 33 | return $messages; 34 | } 35 | //TODO Those would ultimately all come from a config 36 | public static function getItems() : array{ 37 | if (isset(self::$items)){ 38 | return self::$items; 39 | }else{ 40 | $items = [ 41 | 'stone_sword' => [VanillaItems::STONE_SWORD(), 0, 'Uncommon Sword', ToolTier::STONE() ,['damage' => 1]], 42 | 'stone_axe' => [VanillaItems::STONE_AXE(), 0, 'Uncommon Axe', ToolTier::STONE() ,['damage' => 1]], 43 | 'stone_pickaxe' => [VanillaItems::STONE_PICKAXE(), 0, 'Uncommon Pickaxe', ToolTier::STONE() ,['damage' => 1]], 44 | 'stone_shovel' => [VanillaItems::STONE_SHOVEL(), 0, 'Uncommon Shovel', ToolTier::STONE() ,['damage' => 1]], 45 | 'iron_sword' => [VanillaItems::IRON_SWORD(),0,'Rare Sword', ToolTier::IRON() ,['damage' => 1]], 46 | 'iron_axe' => [VanillaItems::IRON_AXE(),0,'Rare Axe', ToolTier::IRON() ,['damage' => 1]], 47 | 'iron_pickaxe' => [VanillaItems::IRON_PICKAXE(),0,'Rare Pickaxe', ToolTier::IRON() ,['damage' => 1]], 48 | 'iron_shovel' => [VanillaItems::IRON_SHOVEL(),0,'Rare Shovel', ToolTier::IRON() ,['damage' => 1]], 49 | 'gold_sword' => [VanillaItems::GOLDEN_SWORD(),0,'Mythic Sword', ToolTier::GOLD() ,['damage' => 1]], 50 | 'gold_axe' => [VanillaItems::GOLDEN_AXE(),0,'Mythic Axe', ToolTier::GOLD() ,['damage' => 1]], 51 | 'gold_pickaxe' => [VanillaItems::GOLDEN_PICKAXE(),0,'Mythic Pickaxe', ToolTier::GOLD() ,['damage' => 1]], 52 | 'gold_shovel' => [VanillaItems::GOLDEN_SHOVEL(),0,'Mythic Shovel', ToolTier::GOLD() ,['damage' => 1]], 53 | 'diamond_sword' => [VanillaItems::DIAMOND_SWORD(),0,'Epic Sword', ToolTier::DIAMOND() ,['damage' =>1]], 54 | 'diamond_axe' => [VanillaItems::DIAMOND_AXE(),0,'Epic Axe', ToolTier::DIAMOND() ,['damage' =>1]], 55 | 'diamond_pickaxe' => [VanillaItems::DIAMOND_PICKAXE(),0,'Epic Pickaxe', ToolTier::DIAMOND() ,['damage' =>1]], 56 | 'diamond_shovel' => [VanillaItems::DIAMOND_SHOVEL(),0,'Epic Shovel', ToolTier::DIAMOND() ,['damage' =>1]], 57 | 'iron_helmet' => [VanillaItems::IRON_HELMET(),0,'Uncommon Helmet',['defense' => 1]], 58 | 'iron_chestplate' => [VanillaItems::IRON_CHESTPLATE(),0,'Uncommon Chestplate',['defense' => 1]], 59 | 'iron_leggings' => [VanillaItems::IRON_LEGGINGS(),0,'Uncommon Leggings',['defense' => 1]], 60 | 'iron_boots' => [VanillaItems::IRON_BOOTS(),0,'Uncommon Boots',['defense' => 1]], 61 | 'gold_helmet' => [VanillaItems::GOLDEN_HELMET(),0,'Mythic Helmet',['defense' => 1]], 62 | 'gold_chestplate' => [VanillaItems::GOLDEN_CHESTPLATE(),0,'Mythic Chestplate',['defense' => 1]], 63 | 'gold_leggings' => [VanillaItems::GOLDEN_LEGGINGS(),0,'Mythic Leggings',['defense' => 1]], 64 | 'gold_boots' => [VanillaItems::GOLDEN_BOOTS(),0,'Mythic Boots',['defense' => 1]], 65 | 'diamond_helmet' => [VanillaItems::DIAMOND_HELMET(),0,'Rare Helmet',['defense' => 1]], 66 | 'diamond_chestplate' => [VanillaItems::DIAMOND_CHESTPLATE(),0,'Rare Chestplate',['defense' => 1]], 67 | 'diamond_leggings' => [VanillaItems::DIAMOND_LEGGINGS(),0,'Rare Leggings',['defense' => 1]], 68 | 'diamond_boots' => [VanillaItems::DIAMOND_BOOTS(),0,'Rare Boots',['defense' => 1]], 69 | 'chain_helmet' => [VanillaItems::CHAINMAIL_HELMET(),0,'Epic Helmet',['defense' => 1]], 70 | 'chain_chestplate' => [VanillaItems::CHAINMAIL_CHESTPLATE(),0,'Epic Chestplate',['defense' => 1]], 71 | 'chain_leggings' => [VanillaItems::CHAINMAIL_LEGGINGS(),0,'Epic Leggings',['defense' => 1]], 72 | 'chain_boots' => [VanillaItems::CHAINMAIL_BOOTS(),0,'Epic Boots',['defense' => 1]], 73 | ]; 74 | self::$items = $items; 75 | return $items; 76 | } 77 | } 78 | public static function translateStringToItem(string $item): array|null 79 | { 80 | $items = self::getItems(); 81 | if (array_key_exists($item, $items)){ 82 | return $items[$item]; 83 | } 84 | return null; 85 | } 86 | } -------------------------------------------------------------------------------- /resources/messages.yml: -------------------------------------------------------------------------------- 1 | level_up_message: "You just leveled up use /rpg to find out more!" 2 | Forms: 3 | UpgradeForm: 4 | title: 'Click to upgrade' 5 | content: '{NL}{RED}Your Stats:{NL}{AQUA}Strength: {STR}{NL}{GREEN}Vitality: {VIT}{NL}{GOLD}Defense: {DEF}{NL}{GRAY}Dexterity: {DEX} {NL} {DARK_RED} Skill Points Left: {SPLEFT}' 6 | buttons: 7 | strength: '§b Upgrade Strength' 8 | vitality: '§b Upgrade Vitality' 9 | defense: '§b Upgrade Defense' 10 | dexterity: '§b Upgrade Dexterity' 11 | exit: 'Exit' 12 | StatsForm: 13 | title: 'Your Stats' 14 | content: '{RED} Your Stats:{NL}{AQUA}Strength: {STR}{NL}{GREEN}Vitality: {VIT}{NL}{GOLD}Defense: {DEF}{NL}{GRAY}Dexterity : {DEX}' 15 | buttons: 16 | exit: 'Exit' 17 | MenuForm: 18 | title: "Menu" 19 | content: '' 20 | buttons: 21 | skills: 'Skills help' 22 | stats: 'Your Stats' 23 | upgrade: 'Upgrade stats' 24 | traits: 'Available Traits' 25 | WelcomeForm: 26 | title: 'Welcome {NAME}!' 27 | content: 'Welcome to the world of Minecraft!{NL}I wish you the best of luck on your quest to defeat the Black Dragon!' 28 | TraitsForm: 29 | title: 'Available traits:' 30 | buttons: 31 | back: 'Back' 32 | TraitHelpForm: 33 | title: '{NAME}' 34 | buttons: 35 | back: 'Back to main menu' 36 | SkillsHelpForm: 37 | title: 'Available Skills:' 38 | buttons: 39 | back: 'Back to main menu' 40 | Skills: 41 | Tank: 42 | SkillHelpForm: 43 | title: 'Tank skill info' 44 | content: 'The "Tank" skill you HP by 10%' 45 | back_button: 'Back to main menu' 46 | prefix: '{AQUA}[Tank]' 47 | description: 'The "Tank" skill increases your max HP by 10 percent.' 48 | unlock_message: ' skill just got unlocked use /rpg to find out more!' 49 | proc_message: '' 50 | level_up: ' skill just leveled up!' 51 | Coinflip: 52 | SkillHelpForm: 53 | title: 'Coinflip skill info' 54 | content: 'The "Coinflip" skill has a percent chance to deal 1.5x damage' 55 | back_button: 'Back to main menu' 56 | prefix: '{AQUA}[Coinflip]' 57 | description: 'The "Coinflip" skill gives you a percent chance to deal 1.5x damage' 58 | unlock_message: 'Coinflip skill just got unlocked use /rpg to find out more!' 59 | proc_message: ' just proced for 50% more damage!' 60 | level_up: ' skill just leveled up!' 61 | DoubleStrike: 62 | SkillHelpForm: 63 | title: 'Double Strike skill info' 64 | content: 'The "Double Strike" gives you a 15% chance to reset attack cooldown' 65 | back_button: 'Back to main menu' 66 | prefix: '{AQUA}[DoubleStrike]' 67 | description: 'The "Double Strike" skill has a 15 percent chance to reset your attack cool-down' 68 | unlock_message: '"Double Strike" skill just got unlocked use /rpg to find out more!' 69 | proc_message: ' Attack cooldown got reset' 70 | level_up: ' just leveled up!' 71 | Fortress: 72 | SkillHelpForm: 73 | title: 'Fortress skill info' 74 | content: 'The "Fortress" skill increases your Defense by 10%' 75 | back_button: 'Back to main menu' 76 | prefix: '{AQUA}[Fortress]' 77 | description: 'The "Fortress" skill increases your damage absorption by 20 percent.' 78 | unlock_message: '"Fortress" skill just got unlocked use /rpg to find out more!' 79 | proc_message: '' 80 | level_up: '"Fortress" skill just leveled up!' 81 | HealingAura: 82 | SkillHelpForm: 83 | title: 'Health Regen skill info' 84 | content: 'The "Health Regen" skill is an AOE constantly healing party members in range' 85 | back_button: 'Back to main menu' 86 | prefix: '{AQUA}[Health regen]' 87 | description: '"Health Regen" is an AOE skill which constantly heals party members' 88 | unlock_message: '"Health Regen" skill just got unlocked use /rpg to find out more!' 89 | proc_message: '' 90 | level_up: ' skill just leveled up!' 91 | Explosion: 92 | SkillHelpForm: 93 | title: 'Explosion skill info' 94 | content: 'The "Explosion" skill makes your arrow explosive on hit with a living' 95 | back_button: 'Back to main menu' 96 | prefix: '{AQUA}[Explosion]' 97 | description: 'Creates explosion on arrow hit' 98 | unlock_message: ' skill just got unlocked use /rpg to find out more!' 99 | proc_message: '' 100 | level_up: ' skill just leveled up!' 101 | Traits: 102 | WoodCutter: 103 | title: 'Wood Cutter Trait' 104 | content: 'The wood cutter trait gives you bonus drops on wood block that has been broken' 105 | buttons: 106 | back: 'Back' 107 | Miner: 108 | title: 'Miner Trait' 109 | content: 'The miner trait gives you bonus drops on broken ores and stone' 110 | buttons: 111 | back: 'Back' 112 | PreciousMaterialsHunter: 113 | title: 'Precious Materials Hunter' 114 | content: 'The precious materials hunter trait gives you bonus drops from valuable ores' 115 | buttons: 116 | back: 'Back' 117 | DiamondHunter: 118 | title: 'Diamond Hunter' 119 | content: 'The diamond hunter trait give you bonus drops from diamond ores' 120 | buttons: 121 | back: 'Back' 122 | MonsterHunter: 123 | title: 'Monster hunter' 124 | content: 'The Monster hunter trait has a percent chance to give you bonus skill point on monster killed' 125 | buttons: 126 | back: 'Back' -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/RPGLike.php: -------------------------------------------------------------------------------- 1 | saveDefaultConfig(); 36 | $this->saveResource('messages.yml'); 37 | $this->setConsts(); 38 | 39 | $messages = (new Config($this->getDataFolder() . 'messages.yml', Config::YAML)); 40 | $players = (new Config($this->getDataFolder() . 'players.yml', Config::YAML)); 41 | 42 | $this->messages = $messages; 43 | $this->players = $players; 44 | $this->discovery = $this->getConfig()->get('discovery'); 45 | 46 | $this->config = $this->getConfig()->getAll(); 47 | date_default_timezone_set($this->config['Hud']['timezone']); 48 | new RPGForms($this); 49 | $this->partySystem = new PartySystem($this); 50 | ItemFactory::createItems([]); 51 | } 52 | 53 | public function onEnable() : void 54 | { 55 | $rpg = new RPGCommand($this); 56 | $this->getServer()->getCommandMap()->register('rpg', $rpg); 57 | 58 | $lvl = new LevelUpCommand(); 59 | $this->getServer()->getCommandMap()->register('lvlup', $lvl); 60 | 61 | $party = new PartyCommand($this); 62 | $this->getServer()->getCommandMap()->register('party', $party); 63 | 64 | $this->getServer()->getPluginManager()->registerEvents(new EventListener($this), $this); 65 | 66 | self::$instance = $this; 67 | 68 | if ($this->config['Hud']['on']) { 69 | $this->getScheduler()->scheduleRepeatingTask(new HudTask($this), $this->config['Hud']['period'] * 20); 70 | } 71 | } 72 | 73 | 74 | public function setConsts(): void 75 | { 76 | $this->consts = [ 77 | "MOTD" => $this->getServer()->getMotd(), 78 | "10SPACE" => str_repeat(" ", 10), 79 | "20SPACE" => str_repeat(" ", 20), 80 | "30SPACE" => str_repeat(" ", 30), 81 | "40SPACE" => str_repeat(" ", 40), 82 | "50SPACE" => str_repeat(" ", 50), 83 | "NL" => TextFormat::EOL, 84 | "BLACK" => TextFormat::BLACK, 85 | "DARK_BLUE" => TextFormat::DARK_BLUE, 86 | "DARK_GREEN" => TextFormat::DARK_GREEN, 87 | "DARK_AQUA" => TextFormat::DARK_AQUA, 88 | "DARK_RED" => TextFormat::DARK_RED, 89 | "DARK_PURPLE" => TextFormat::DARK_PURPLE, 90 | "GOLD" => TextFormat::GOLD, 91 | "GRAY" => TextFormat::GRAY, 92 | "DARK_GRAY" => TextFormat::DARK_GRAY, 93 | "BLUE" => TextFormat::BLUE, 94 | "GREEN" => TextFormat::GREEN, 95 | "AQUA" => TextFormat::AQUA, 96 | "RED" => TextFormat::RED, 97 | "LIGHT_PURPLE" => TextFormat::LIGHT_PURPLE, 98 | "YELLOW" => TextFormat::YELLOW, 99 | "WHITE" => TextFormat::WHITE, 100 | "OBFUSCATED" => TextFormat::OBFUSCATED, 101 | "BOLD" => TextFormat::BOLD, 102 | "STRIKETHROUGH" => TextFormat::STRIKETHROUGH, 103 | "UNDERLINE" => TextFormat::UNDERLINE, 104 | "ITALIC" => TextFormat::ITALIC, 105 | "RESET" => TextFormat::RESET, 106 | ]; 107 | } 108 | 109 | public function getHUD(RPGPlayer $player): string 110 | { 111 | $item = $player->getInventory()->getItemInHand(); 112 | 113 | $playerSpecific = [ 114 | 'NAME' => $player->getName(), 115 | 'HEALTH' => $player->getHealth(), 116 | 'MAXHP' => $player->getMaxHealth(), 117 | 'DAMAGE' => $item->getAttackPoints() + round($player->getSTRBonus()), 118 | 'MOVEMENTSPEED' => round($player->getMovementSpeed(), 4), 119 | 'DEFENSE' => $player->getArmorPoints() + $player->getDEFBonus(), 120 | 'TICKS' => $player->getServer()->getTicksPerSecondAverage(), 121 | 'LEVEL' => $player->getWorld()->getDisplayName(), 122 | 'XPLEVEL' => $player->getXpManager()->getXpLevel(), 123 | 'TIME' => date('h:i') 124 | ]; 125 | $keywords = array_merge($playerSpecific, $playerSpecific); 126 | return Utils::parseKeywords($keywords, $this->config['Hud']['message']); 127 | 128 | } 129 | 130 | public function getMessages(): Config 131 | { 132 | return $this->messages; 133 | } 134 | public function getPlayers() : Config 135 | { 136 | return $this->players; 137 | } 138 | public function getDiscovery() : bool{ 139 | return $this->discovery; 140 | } 141 | public function getTieredItems(){ 142 | return $this->config['ItemTiers']; 143 | } 144 | public static function getInstance(): RPGLike 145 | { 146 | return self::$instance; 147 | } 148 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Traits/BaseTrait.php: -------------------------------------------------------------------------------- 1 | name = $name; 25 | $this->blocks = $blocks; 26 | $this->levels = $levels; 27 | $this->trait_action = $trait_action; 28 | $this->restorePlayerTrait($blockBreaks); 29 | } 30 | 31 | public function getName(): string 32 | { 33 | return $this->name; 34 | } 35 | 36 | public function setName(string $name): void 37 | { 38 | $this->name = $name; 39 | } 40 | 41 | public function getLevels(): array 42 | { 43 | return $this->levels; 44 | } 45 | 46 | public function setLevels(array $levels): void 47 | { 48 | $this->levels = $levels; 49 | } 50 | 51 | public function getBlocks(): array 52 | { 53 | return $this->blocks; 54 | } 55 | 56 | public function setBlocks(array $blocks) 57 | { 58 | $this->blocks = $blocks; 59 | } 60 | 61 | public function blockBreak(BlockBreakEvent $event) 62 | { 63 | if ($this->trait_action == 'break'){ 64 | if (in_array($event->getBlock()->getName(), $this->blocks)) { 65 | $this->count += 1; 66 | $this->checkLevel(); 67 | $drops = $event->getDrops(); 68 | $drop_chance = $this->getBlockDropChance(); 69 | if ($drop_chance > 0 && $this->tryChance($drop_chance)){ 70 | $drops[] = $drops[0]; 71 | $event->setDrops($drops); 72 | $player = $event->getPlayer(); 73 | $position = $player->getPosition(); 74 | $player->getWorld()->addSound($position, new PopSound); 75 | } 76 | } 77 | } 78 | } 79 | public function blockPickup(PlayerBlockPickEvent $event){ 80 | if ($this->trait_action == "pickup"){ 81 | $player = $event->getPlayer(); 82 | if ($player instanceof RPGPlayer){ 83 | if (in_array($event->getBlock()->getName(), $this->blocks)) { 84 | $this->count += 1; 85 | $this->checkLevel(); 86 | $drop_chance = $this->getBlockDropChance(); 87 | if ($drop_chance > 0 && $this->tryChance($drop_chance)){ 88 | $event->getResultItem()->setCount($event->getResultItem()->getCount() + 1); 89 | $event->getPlayer()->sendMessage('You just got 1 additional drop!'); 90 | 91 | } 92 | } 93 | } 94 | } 95 | } 96 | public function entityKill(EntityDamageByEntityEvent $event){ 97 | if ($this->trait_action == "kill"){ 98 | $damager = $event->getDamager(); 99 | $damaged_target = $event->getEntity(); 100 | $damaged_target_health = $damaged_target->getHealth(); 101 | $damage = $event->getFinalDamage(); 102 | if (($damaged_target_health - $damage) <= 0){ 103 | if ($damager instanceof RPGPlayer){ 104 | $this->count += 1; 105 | $this->checkLevel(); 106 | $drop_chance = $this->getBlockDropChance(); 107 | if ($drop_chance > 0 && $this->tryChance($drop_chance)){ 108 | $damager->spleft += 1; 109 | $damager->sendMessage("You just got 1 skill point"); // TODO add economy support?? 110 | } 111 | } 112 | } 113 | } 114 | } 115 | 116 | public function getBlockBreaks(): int 117 | { 118 | return $this->count; 119 | } 120 | 121 | public function getCurrentLevel(): int 122 | { 123 | return $this->currentLevel; 124 | } 125 | 126 | public function getBlockDropChance(): int 127 | { 128 | if ($this->getCurrentLevel() == 0){ 129 | return 0; 130 | } 131 | return $this->levels[$this->getCurrentLevel()]['drop_chance']; 132 | } 133 | 134 | public function restorePlayerTrait(int $blockBreaks) 135 | { 136 | $this->count = $blockBreaks; 137 | $this->checkLevel(); 138 | } 139 | public function checkLevel(){ 140 | foreach ($this->levels as $key => $level) { 141 | if ($this->count > $level['requirement']) { 142 | $this->currentLevel = $key; 143 | if (!$this->unlocked){ 144 | $this->setIsUnlocked(true); 145 | } 146 | } 147 | } 148 | } 149 | public function tryChance(int $drop_chance): bool 150 | { 151 | if (mt_rand(0, 99) < $drop_chance){ 152 | return true; 153 | } 154 | return false; 155 | } 156 | 157 | /** 158 | * @return bool 159 | */ 160 | public function isUnlocked(): bool 161 | { 162 | return $this->unlocked; 163 | } 164 | 165 | /** 166 | * @param bool $unlocked 167 | */ 168 | public function setIsUnlocked(bool $unlocked): void 169 | { 170 | $this->unlocked = $unlocked; 171 | } 172 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

RPGLike

5 | 6 |

7 | This plugin aims to add higher RPG feel to Minecraft adding attributes and skills. 8 |
9 |
10 |
11 | Report Bug 12 | · 13 | Request Feature 14 |

15 |
16 |
17 | 18 | 19 | 20 | 21 |
22 |

Table of Contents

23 |
    24 |
  1. 25 | About The Project 26 |
  2. 27 |
  3. 28 | Getting Started 29 | 32 |
  4. 33 |
  5. Usage
  6. 34 |
  7. Roadmap
  8. 35 |
36 |
37 | 38 | 39 | 40 | 41 |
42 | 43 | ## About The Project 44 | Started this as a side project. I'm currently working full time so update frequency may vary
45 | depending on my workload. My aspirations for this project is ultimately creating a mini-game
46 | with as much as storyline and quests so that's something to look forward.Until then
47 | we have something that slightly enhances player in-game experience but it's still fun.
48 | I'm grateful for every contribution you make. Even as much as testing takes a lot of my time which I don't have. 49 | 50 |
51 | 52 | 53 | 54 |
55 | 56 | ## Getting Started 57 | 58 |
59 | 60 | ### Installation 61 |
62 | Just open the releases tab and download one of the official releases, then place it in the plugins folder. 63 |
64 | 65 | 66 | ## Usage 67 | 68 |
69 | Aftrer installation and initial start you can check the plugin data folder of the plugin
70 | to configure the plugin to your liking.
71 | 72 | ### messages.yml:
73 | Here you can change every string that's displayed to the player. 74 | ### players.yml 75 | Here are all the players that've connected to the server with all their attributes. 76 | ### config.yml 77 | Here are all the settings of the plugin. Here is a quick rundown of the settings you'll need.
78 | #### keep-xp:
79 | Pretty self-explanatory this setting has 2 values. true or false. Controls whether the player
80 | loses all attributes,skills,traits and levels when they've died. 81 | #### discovery: 82 | This setting has 2 values true or false. When set to true it will hide every skill and trait until the
83 | player have unlocked it.
84 | When set to false all skills and traits will be visible in the /rpg menu. 85 | #### Hud 86 | Here you can turn on or off the included HUD. Change the message it transmits, its period in which its transmitted
87 | and the timezone of the server. 88 | #### Skills 89 | Here are all the skills the plugin has. Here is the Tank skills for example.
90 | If you look at [levels]() you will see under it all the available levels for this particular skill.
91 | You are free to add new levels to your liking. Just be sure to keep the spacing of it. As a hint you can copy level 2
92 | place it right under it. From there you can simply change 2 => 3 and change its values. Now here are the values
93 | under [unlock]() you have first the Attribute (VIT,DEX,STR or DEF) you can freely change this as you like.
94 | on the right of the Attribute you have the points a player must have in order to unlock it. If you want to add more
95 | Attributes to unlock a skill just add a new line under unlock with the next requirement in the format [Attribute]() : [Points]()
96 | Under [unlock]() you have [chance]() from there you can configure the chance at which a skill's effect will trigger itself.
97 |
98 | That about explains the [levels]() the next one is [range]() when an active or passive skill has AOE range this is where
99 | it's configured. The value is in blocks from the source player.
100 |
101 | [max_entities_in_range]() this option allows to set the maximum entities in the [range]() of the skill that will be affected
102 | by the effect of the skill. The value is an integer.
103 |
104 | [is_aoe]() controls whether the skills is aoe or not. If this option is set to true please specify [range]() and [max_entities_in_range]() 105 |
106 | [cooldown]() controls the cooldown of an active ability it's value is an integer in seconds
107 |
108 | [is_active]() has 2 values true and false, when set to true the skill is considered active and should have a [cooldown]().
109 |
110 | 111 | #### Modifiers 112 | Those are used to modify how much a player gains per point in an attribute. For example DEX with a value of 0.005 will be multiplied by the points in DEX. 113 | Try experimenting with these to achieve the desired strength of attributes. 114 | 115 | 116 |
117 | 118 | #### Traits 119 | Here you can create whatever player traits you want! The constraints however are
120 | that you the counter of each trait is connected to the 3 types of [action]() a trait currently
121 | support. Those are "break", "kill", "pickup". "break" counts each block that's been broken
122 | by the player. Those blocks can be set from the [blocks]() option which takes values separated by a comma. 123 | Same with the others "pickup" counts blocks or any items picked up. And kill counts entity killed
124 | that includes players. 125 | [levels]() is the same as in the skills [section](). 126 | 127 | 128 | ## Roadmap 129 | 130 |
131 | 132 | ### Current objectives: 133 | * Finishing the trait system with the 3 included actions.
134 | * Active bug hunting 135 | * More configuration for settings (there is still some hard coded stuff) 136 | * Adding permissions and more control from in-game commands or maybe an admin form? 137 | * Party system
138 | After this have been completed an official release will be made until then every release should be considered a heavy WIP. 139 |
140 | You can contact me any time on Discord StrawberryMilk#3229 141 | 142 | See the [open issues](https://github.com/MHIGists/RPGLike/issues) for a list of proposed features (and known issues). 143 | -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/EventListener.php: -------------------------------------------------------------------------------- 1 | main = $rpg; 32 | } 33 | 34 | public function playerCreate(PlayerCreationEvent $event) 35 | { 36 | $event->setPlayerClass("TheClimbing\RPGLike\Players\RPGPlayer"); 37 | } 38 | 39 | public function playerJoin(PlayerJoinEvent $event) 40 | { 41 | $player = $event->getPlayer(); 42 | if ($player instanceof RPGPlayer) { 43 | $joinTime = (((int)(microtime(true) * 1000) - $player->getFirstPlayed())); 44 | if (($joinTime / 1000) < 300) { 45 | RPGForms::welcomeForm($player); 46 | } 47 | $player->restorePlayerVariables(); 48 | $player->getInventory()->addItem(new UncommonTierItem(ItemIds::STONE_SWORD, 1, 'Uncommon Sword', ['damage' => 1])); //TODO 49 | } 50 | } 51 | 52 | public function blockPickup(PlayerBlockPickEvent $event) 53 | { 54 | $player = $event->getPlayer(); 55 | if ($player instanceof RPGPlayer) { 56 | foreach ($player->getTraits() as $trait) { 57 | $trait->blockPickup($event); 58 | } 59 | } 60 | } 61 | 62 | public function onMove(PlayerMoveEvent $event) 63 | { 64 | $player = $event->getPlayer(); 65 | if ($player instanceof RPGPlayer) { 66 | $playerSkills = $player->getSkills(); 67 | foreach ($playerSkills as $playerSkill) { 68 | if ($playerSkill->isUnlocked()) { 69 | if ($playerSkill->isAOE()) { 70 | $playerSkill->checkRange(); 71 | } 72 | } 73 | } 74 | } 75 | } 76 | 77 | public function playerDeath(PlayerDeathEvent $event) 78 | { 79 | $player = $event->getPlayer(); 80 | if ($player instanceof RPGPlayer) { 81 | if ($this->main->config['keep-xp'] === true) { 82 | $event->setXpDropAmount(0); 83 | } else { 84 | $player->setDEX(1); 85 | $player->setSTR(1); 86 | $player->setVIT(1); 87 | $player->setDEF(1); 88 | $player->resetSkills(); 89 | $player->setExperienceLevel(0); 90 | } 91 | } 92 | } 93 | 94 | public function arrowHit(ProjectileHitEntityEvent $event) 95 | { 96 | $player = $event->getEntity()->getOwningEntity(); 97 | $entity_hit = $event->getEntityHit(); 98 | if ($player instanceof RPGPlayer) { 99 | $explosion = $player->getSkill('Explosion'); 100 | if ($explosion->isUnlocked()) { 101 | $explosion->damageEvent($player, $entity_hit); 102 | } 103 | } 104 | } 105 | 106 | public function dealDamageEvent(EntityDamageByEntityEvent $event) 107 | { 108 | $damager = $event->getDamager(); 109 | $receiver = $event->getEntity(); 110 | if ($damager instanceof RPGPlayer && $receiver instanceof RPGPlayer) { 111 | if ($damager->getParty()){ 112 | if($damager->getParty()->playerInThisParty($receiver->getName())){ 113 | $event->cancel(); 114 | } 115 | } 116 | $coinflip = $damager->getSkill('Coinflip'); 117 | $doublestrike = $damager->getSkill('DoubleStrike'); 118 | $damager->applyDamageBonus($event); 119 | $damager->applyDefenseBonus($event); 120 | if ($coinflip->isUnlocked()) { 121 | $coinflip->passiveEffect($event); 122 | } 123 | if ($doublestrike->isUnlocked()) { 124 | $doublestrike->passiveEffect($event); 125 | } 126 | foreach ($damager->getTraits() as $trait) { 127 | $trait->entityKill($event); 128 | } 129 | } 130 | } 131 | 132 | public function onLevelUp(PlayerExperienceChangeEvent $event) 133 | { 134 | $player = $event->getEntity(); 135 | if ($player instanceof RPGPlayer) { 136 | $player->shareEXP($event); 137 | $new_lvl = $event->getNewLevel(); 138 | $old_level = $player->getXpLvl(); 139 | if ($new_lvl !== null) { 140 | if ($new_lvl > $old_level) { 141 | 142 | $player->xplevel = $new_lvl; 143 | $spleft = $new_lvl - $old_level; 144 | $player->setSPleft($player->getSPleft() + $spleft); 145 | $player->setHealth($player->getMaxHealth()); 146 | $player->getHungerManager()->setFood($player->getHungerManager()->getMaxFood()); 147 | $player->setAirSupplyTicks($player->getMaxAirSupplyTicks()); 148 | if ($player->xplevel == 1){ 149 | $player->sendMessage(Utils::parseKeywords(RPGLike::getInstance()->consts, RPGLike::getInstance()->getMessages()->get('level_up_message'))); 150 | } 151 | } 152 | } 153 | } 154 | 155 | } 156 | 157 | public function onRespawn(PlayerRespawnEvent $event) 158 | { 159 | $player = $event->getPlayer(); 160 | if ($player instanceof RPGPlayer) { 161 | $player->applyVitalityBonus(); 162 | $player->applyDexterityBonus(); 163 | } 164 | } 165 | 166 | public function blockDestroy(BlockBreakEvent $event) 167 | { 168 | $player = $event->getPlayer(); 169 | if ($player instanceof RPGPlayer) { 170 | foreach ($player->getTraits() as $trait) { 171 | $trait->blockBreak($event); 172 | } 173 | } 174 | } 175 | 176 | public function onLeave(PlayerQuitEvent $event) 177 | { 178 | $event->getPlayer()->savePlayerVariables(); 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Forms/RPGForms.php: -------------------------------------------------------------------------------- 1 | spleft <= 0) { 26 | $player->setSPleft(0); 27 | self::statsForm($player); 28 | $player->checkForSkills(); 29 | if ($player->getSkill('Tank')->isUnlocked()) { 30 | $player->getSkill('Tank')->passiveEffect($player); 31 | } 32 | if ($player->getSkill('Fortress')->isUnlocked()) { 33 | $player->getSkill('Fortress')->passiveEffect($player); 34 | } 35 | $player->checkSkillLevel(); 36 | $player->setSPleft($player->spleft); 37 | return; 38 | } 39 | $messages = self::parseMessages($player, 'UpgradeForm', $player->spleft); 40 | 41 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($player) { 42 | switch ($data) { 43 | case "strength": 44 | $player->setSTR($player->getSTR() + 1); 45 | $player->spleft--; 46 | break; 47 | case "vitality": 48 | $player->setVIT($player->getVIT() + 1); 49 | $player->spleft--; 50 | $player->applyVitalityBonus(); 51 | break; 52 | case "defense": 53 | $player->setDEF($player->getDEF() + 1); 54 | $player->spleft--; 55 | break; 56 | case "dexterity": 57 | $player->setDEX($player->getDEX() + 1); 58 | $player->spleft--; 59 | break; 60 | case "exit": 61 | default: 62 | return; 63 | } 64 | self::upgradeStatsForm($player); 65 | 66 | }); 67 | 68 | $form->setTitle($messages['title']); 69 | $form->setContent($messages['content']); 70 | foreach ($messages['buttons'] as $key => $button) { 71 | $form->addButton($button, -1, '', $key); 72 | } 73 | $player->sendForm($form); 74 | } 75 | 76 | public static function skillHelpForm(RPGPlayer $player, ?BaseSkill $skill) 77 | { 78 | $messages = $skill->getMessages()['SkillHelpForm']; 79 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($player) { 80 | if ($data == 'Back') { 81 | self::skillsHelpForm($player); 82 | } 83 | }); 84 | $form->setTitle($skill->getName() . $messages['title']); 85 | $form->setContent($messages['content']); 86 | $form->addButton($messages['back_button'], -1, '', 'Back'); 87 | $player->sendForm($form); 88 | } 89 | 90 | public static function statsForm(RPGPlayer $player, bool $back = false) 91 | { 92 | $messages = self::parseMessages(($player), 'StatsForm'); 93 | 94 | $form = new SimpleForm(function (RPGPlayer $p, $data) use ($player) { 95 | if ($data == 'back') { 96 | self::menuForm($player); 97 | } 98 | }); 99 | $form->setTitle($messages['title']); 100 | $form->setContent($messages['content']); 101 | foreach ($messages['buttons'] as $key => $message) { 102 | $form->addButton($message, -1, '', $key); 103 | } 104 | if ($back) { 105 | $form->addButton('Back', -1, '', 'back'); 106 | } 107 | $player->sendForm($form); 108 | 109 | } 110 | 111 | public static function menuForm(RPGPlayer $player) 112 | { 113 | $menuStrings = self::parseMessages($player, 'MenuForm'); 114 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($player) { 115 | switch ($data) { 116 | case "skills": 117 | self::skillsHelpForm($player); 118 | break; 119 | case "stats": 120 | self::statsForm($player, true); 121 | break; 122 | case "upgrade": 123 | self::upgradeStatsForm($player); 124 | break; 125 | case "traits": 126 | self::traitsForm($player); 127 | } 128 | }); 129 | $form->setTitle($menuStrings['title']); 130 | foreach ($menuStrings['buttons'] as $key => $buttonText) { 131 | $form->addButton($buttonText, -1, '', $key); 132 | } 133 | if (array_key_exists('content', $menuStrings)) { 134 | $form->setContent($menuStrings['content']); 135 | } 136 | $player->sendForm($form); 137 | } 138 | 139 | public static function skillsHelpForm(RPGPlayer $player) 140 | { 141 | $skills = $player->getSkills(); 142 | $messages = self::parseMessages($player, 'SkillsHelpForm'); 143 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($skills, $player) { 144 | foreach ($skills as $key => $skill) { 145 | if ($key == $data) { 146 | self::skillHelpForm($player, $skill); 147 | } 148 | } 149 | if ($data == 'back') { 150 | self::menuForm($player); 151 | } 152 | }); 153 | $form->setTitle($messages['title']); 154 | foreach ($skills as $key => $skill) { 155 | if (!$skill->isUnlocked()){ 156 | continue; 157 | } 158 | $form->addButton($key, -1, '', $key); 159 | } 160 | $form->addButton($messages['buttons']['back'], -1, '', 'back'); 161 | $player->sendForm($form); 162 | } 163 | public static function welcomeForm(RPGPLayer $player){ 164 | $messages = self::parseMessages($player, 'WelcomeForm'); 165 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($player){}); 166 | $form->setTitle($messages['title']); 167 | $form->setContent($messages['content']); 168 | $player->sendForm($form); 169 | } 170 | public static function traitsForm(RPGPlayer $player){ 171 | $traits = $player->getTraits(); 172 | $messages = self::parseMessages($player, 'TraitsForm'); 173 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($traits, $player){ 174 | foreach ($traits as $key => $trait) { 175 | if ($key == $data){ 176 | self::traitHelpForm($player, $trait); 177 | } 178 | } 179 | if ($data == 'back') { 180 | self::menuForm($player); 181 | } 182 | }); 183 | $form->setTitle($messages['title']); 184 | foreach ($traits as $key => $trait) { 185 | if($trait->isUnlocked()){ 186 | $form->addButton($key,-1,'', $key); 187 | } 188 | } 189 | $form->addButton($messages['buttons']['back'],-1,'','back'); 190 | $player->sendForm($form); 191 | } 192 | public static function traitHelpForm(RPGPlayer $player, BaseTrait $trait){ 193 | $messages = self::parseMessages($player,'TraitHelpForm'); 194 | $form = new SimpleForm(function (RPGPlayer $pl, $data) use ($player) { 195 | if ($data == 'Back') { 196 | self::traitsForm($player); 197 | } 198 | }); 199 | $form->setTitle($trait->getName() . $messages['title']); 200 | $form->setContent($messages['content']); 201 | $form->addButton($messages['buttons']['back'], -1, '', 'Back'); 202 | $player->sendForm($form); 203 | } 204 | 205 | public static function parseMessages(RPGPlayer $player, string $type, int $spleft = 0): array 206 | { 207 | $messages = self::$main->getMessages()->get('Forms')[$type]; 208 | $stats = $player->getAttributes(); 209 | $stats['PLAYER'] = $player->getName(); 210 | $stats['SPLEFT'] = $spleft; 211 | 212 | $joined = array_merge($stats, self::$main->consts); 213 | return Utils::parseArrayKeywords($joined, $messages); 214 | } 215 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Skills/BaseSkill.php: -------------------------------------------------------------------------------- 1 | getMessages()->get('Skills'); 62 | $this->owner = $owner; 63 | $this->name = $name; 64 | $this->effect = $effect; 65 | $this->messages = $messages[$this->getName()]; 66 | $this->skillConfig = $owner->getConfig()->getNested('Skills')[$this->getName()]; 67 | $this->getSkillConfig(); 68 | } 69 | 70 | public function unlock(bool $restore = false): void 71 | { 72 | $this->unlocked = true; 73 | if (!$restore) { 74 | $this->owner->sendMessage(Utils::parseKeywords(RPGLike::getInstance()->consts, $this->getSkillPrefix() . $this->getUnlockMessage())); 75 | } 76 | } 77 | 78 | public function isUnlocked(): bool 79 | { 80 | return $this->unlocked; 81 | } 82 | 83 | public function reset() 84 | { 85 | $this->unlocked = false; 86 | $this->skillLevel = 1; 87 | } 88 | 89 | public function checkLevel(bool $restore = false) 90 | { 91 | foreach ($this->skillLevels as $key => $value) { 92 | $criteria = count($value['unlock']); 93 | $met_criteria = 0; 94 | foreach ($value['unlock'] as $key1 => $item) { 95 | if ($this->owner->getAttribute($key1) >= $item) { 96 | $met_criteria++; 97 | } 98 | } 99 | if ($criteria <= $met_criteria) { 100 | if ($this->skillLevel < $key) { 101 | $this->skillLevel = $key; 102 | if (!$restore) { 103 | $this->transmitLevelUpMessage(); 104 | } 105 | } 106 | } 107 | } 108 | } 109 | 110 | public function isActive(): bool 111 | { 112 | return $this->is_active; 113 | } 114 | 115 | /** 116 | * @param string $name 117 | */ 118 | public function setName(string $name): void 119 | { 120 | $this->name = $name; 121 | } 122 | 123 | /** 124 | * @return string 125 | */ 126 | public function getName(): string 127 | { 128 | return $this->name; 129 | } 130 | 131 | public function setCooldownTime(int $cooldown): void 132 | { 133 | $this->cooldown = $cooldown; 134 | } 135 | 136 | /** 137 | * @return int 138 | */ 139 | public function getCooldownTime(): int 140 | { 141 | return $this->cooldown; 142 | } 143 | 144 | public function setOnCooldown(): void 145 | { 146 | $this->onCooldown = true; 147 | } 148 | public function setCooldownTask() 149 | { 150 | RPGLike::getInstance()->getScheduler()->scheduleDelayedTask(new CooldownTask($this->owner, $this->getName()), $this->getCooldownTime()); 151 | $this->cdStartTime = time(); 152 | } 153 | 154 | public function isOnCooldown(): bool 155 | { 156 | return $this->onCooldown; 157 | } 158 | 159 | public function removeCooldown(): void 160 | { 161 | $this->onCooldown = false; 162 | } 163 | 164 | public function getRemainingCooldown(string $format): string 165 | { 166 | $timediff = time() - $this->cdStartTime; 167 | $cdsecs = ($this->getCooldownTime() / 20) - $timediff; 168 | $cdmins = $cdsecs / 60; 169 | if ($cdmins < 1) { 170 | $cdmins = 0; 171 | } 172 | $cdhours = $cdmins / 60; 173 | if ($cdhours < 1) { 174 | $cdhours = 0; 175 | } 176 | $format = str_split($format); 177 | foreach ($format as $key => $item) { 178 | if ($item == 'H') { 179 | $format[$key] = round($cdhours, PHP_ROUND_HALF_DOWN); 180 | } 181 | if ($item == 'M') { 182 | $format[$key] = round($cdmins, PHP_ROUND_HALF_DOWN); 183 | } 184 | if ($item == 'S') { 185 | $format[$key] = round($cdsecs, PHP_ROUND_HALF_DOWN); 186 | } 187 | } 188 | return implode('', $format); 189 | } 190 | 191 | public function isAOE(): bool 192 | { 193 | return $this->isAOE; 194 | } 195 | 196 | protected function setRange(int $range): void 197 | { 198 | $this->range = $range; 199 | } 200 | 201 | /** 202 | * @return int 203 | */ 204 | public function getRange(): int 205 | { 206 | return $this->range; 207 | } 208 | 209 | 210 | public function getBaseUnlock(): array 211 | { 212 | return $this->skillLevels[1]['unlock']; 213 | } 214 | protected function setEffect(int $id): void 215 | { 216 | $this->effect = $id; 217 | } 218 | 219 | /** 220 | * @return int|null 221 | */ 222 | public function getEffect(): ?int 223 | { 224 | return $this->effect; 225 | } 226 | 227 | /** 228 | * @return int 229 | */ 230 | public function getSkillLevel(): int 231 | { 232 | return $this->skillLevel; 233 | } 234 | 235 | /** 236 | * Basically taken from source. 237 | * 238 | * @param Vector3 $pos 239 | * @param int|null $maxDistance 240 | * @param World $world 241 | * @param int $maxEntities 242 | * @param bool $includeDead 243 | * 244 | * @return array 245 | */ 246 | public function getNearestEntities(Vector3 $pos, ?int $maxDistance, World $world, int $maxEntities, bool $includeDead = false): array 247 | { 248 | $nearby = []; 249 | 250 | $minX = ((int)floor($pos->x - $maxDistance)) >> 4; 251 | $maxX = ((int)floor($pos->x + $maxDistance)) >> 4; 252 | $minZ = ((int)floor($pos->z - $maxDistance)) >> 4; 253 | $maxZ = ((int)floor($pos->z + $maxDistance)) >> 4; 254 | 255 | $currentTargetDistSq = $maxDistance ** 2; 256 | 257 | for ($x = $minX; $x <= $maxX; ++$x) { 258 | for ($z = $minZ; $z <= $maxZ; ++$z) { 259 | $entities = ($chunk = $world->getChunk($x, $z)) !== null ? $world->getChunkEntities($x, $z) : []; 260 | if (count($entities) > $maxEntities) { 261 | $entities = array_slice($entities, 0, $maxEntities); 262 | } 263 | foreach ($entities as $entity) { 264 | if (!($entity instanceof RPGPlayer) or $entity->isClosed() or $entity->isFlaggedForDespawn() or (!$includeDead and !$entity->isAlive())) { 265 | continue; 266 | } 267 | $distSq = $entity->getDirectionVector()->distanceSquared($pos); 268 | if ($distSq < $currentTargetDistSq) { 269 | $currentTargetDistSq = $distSq; 270 | $nearby[] = $entity; 271 | } 272 | } 273 | } 274 | } 275 | return $nearby; 276 | } 277 | public function getSkillDescription(): string 278 | { 279 | return $this->messages['description']; 280 | } 281 | 282 | public function getSkillUnlockConditions(): string 283 | { 284 | return $this->messages['unlocks']; 285 | } 286 | 287 | public function getSkillProcMessage(): string 288 | { 289 | return $this->messages['proc_message']; 290 | } 291 | 292 | public function getSkillPrefix(): string 293 | { 294 | return $this->messages['prefix']; 295 | } 296 | 297 | public function getSkillLevelUpMessage() 298 | { 299 | return $this->messages['level_up']; 300 | } 301 | 302 | public function transmitProcMessage(): void 303 | { 304 | $this->owner->sendMessage(Utils::parseKeywords(RPGLike::getInstance()->consts, $this->getSkillPrefix() . $this->getSkillProcMessage())); 305 | } 306 | 307 | public function transmitLevelUpMessage() 308 | { 309 | $this->owner->sendMessage(Utils::parseKeywords(RPGLike::getInstance()->consts, $this->getSkillPrefix() . $this->getSkillLevelUpMessage() . 'to level ' . $this->getSkillLevel())); 310 | } 311 | 312 | public function getMaximumEntitiesInRange() 313 | { 314 | return 1 + $this->skillConfig['max_entities_in_range']; 315 | } 316 | 317 | public function getMessages(): array 318 | { 319 | return $this->messages; 320 | } 321 | 322 | public function getUnlockMessage(): string 323 | { 324 | return $this->messages['unlock_message']; 325 | } 326 | 327 | public function getSkillConfig() 328 | { 329 | $this->skillLevels = $this->skillConfig['levels']; 330 | $this->isAOE = $this->skillConfig['is_aoe']; 331 | $this->cooldown = $this->skillConfig['cooldown']; 332 | $this->is_active = $this->skillConfig['is_active']; 333 | $this->range = $this->skillConfig['range']; 334 | } 335 | 336 | } -------------------------------------------------------------------------------- /src/TheClimbing/RPGLike/Players/RPGPlayer.php: -------------------------------------------------------------------------------- 1 | config = RPGLike::getInstance()->getConfig(); 73 | $modifiers = $this->getModifiers(); 74 | if ($modifiers) { 75 | $this->setDEFModifier($modifiers['DEF']); 76 | $this->setVITModifier($modifiers['VIT']); 77 | $this->setSTRModifier($modifiers['STR']); 78 | $this->setDEXModifier($modifiers['DEX']); 79 | } 80 | $this->calcVITBonus(); 81 | $this->calcDEXBonus(); 82 | $this->addSkills(); 83 | $traits = $this->config->getNested("Traits"); 84 | $players = RPGLike::getInstance()->getPlayers()->getAll(); 85 | $block_breaks = 0; 86 | if (array_key_exists($this->getName(), $players)) { 87 | $block_breaks = $players[$this->getName()]['block_breaks']; 88 | } 89 | foreach ($traits as $key => $value) { 90 | if ($block_breaks != 0) { 91 | $this->traits[$key] = new BaseTrait($key, $value['blocks'], $value['levels'], $value['action'], $block_breaks); 92 | } else { 93 | $this->traits[$key] = new BaseTrait($key, $value['blocks'], $value['levels'], $value['action']); 94 | } 95 | } 96 | } 97 | 98 | protected function onDeath(): void 99 | { 100 | if ($this->getConfig()->get('keep-xp')) { 101 | $ev = new PlayerDeathEvent($this, $this->getDrops(), 0, null); 102 | } else { 103 | $ev = new PlayerDeathEvent($this, $this->getDrops(), $this->getXpDropAmount(), null); 104 | } 105 | $ev->call(); 106 | 107 | if (!$ev->getKeepInventory()) { 108 | foreach ($ev->getDrops() as $item) { 109 | $this->getWorld()->dropItem($this->location, $item); 110 | } 111 | 112 | if ($this->inventory !== null) { 113 | $this->inventory->setHeldItemIndex(0); 114 | $this->inventory->clearAll(); 115 | } 116 | $this->armorInventory?->clearAll(); 117 | $this->offHandInventory?->clearAll(); 118 | } 119 | if (!$this->getConfig()->get('keep-xp')) { 120 | $this->getWorld()->dropExperience($this->location, $ev->getXpDropAmount()); 121 | $this->xpManager->setXpAndProgress(0, 0.0); 122 | } 123 | if ($ev->getDeathMessage() != "") { 124 | $this->server->broadcastMessage($ev->getDeathMessage()); 125 | } 126 | 127 | $this->startDeathAnimation(); 128 | 129 | $this->getNetworkSession()->onServerDeath(); 130 | } 131 | 132 | protected function actuallyRespawn(): void 133 | { 134 | $this->logger->debug("Waiting for spawn terrain generation for respawn"); 135 | $spawn = $this->getSpawn(); 136 | $spawn->getWorld()->orderChunkPopulation($spawn->getFloorX() >> Chunk::COORD_BIT_SIZE, $spawn->getFloorZ() >> Chunk::COORD_BIT_SIZE, null)->onCompletion( 137 | function () use ($spawn): void { 138 | if (!$this->isConnected()) { 139 | return; 140 | } 141 | $this->logger->debug("Spawn terrain generation done, completing respawn"); 142 | $spawn = $spawn->getWorld()->getSafeSpawn($spawn); 143 | $ev = new PlayerRespawnEvent($this, $spawn); 144 | $ev->call(); 145 | 146 | $realSpawn = Position::fromObject($ev->getRespawnPosition()->add(0.5, 0, 0.5), $ev->getRespawnPosition()->getWorld()); 147 | $this->teleport($realSpawn); 148 | 149 | $this->setSprinting(false); 150 | $this->setSneaking(false); 151 | $this->setFlying(false); 152 | 153 | $this->extinguish(); 154 | $this->setAirSupplyTicks($this->getMaxAirSupplyTicks()); 155 | $this->deadTicks = 0; 156 | $this->noDamageTicks = 60; 157 | 158 | $this->effectManager->clear(); 159 | $this->setHealth($this->getMaxHealth()); 160 | 161 | foreach ($this->attributeMap->getAll() as $attr) { 162 | if ($attr->getId() === Attribute::EXPERIENCE or $attr->getId() === Attribute::EXPERIENCE_LEVEL) { 163 | $attr->markSynchronized(false); 164 | continue; 165 | } 166 | $attr->resetToDefault(); 167 | } 168 | 169 | $this->spawnToAll(); 170 | $this->scheduleUpdate(); 171 | 172 | $this->getNetworkSession()->onServerRespawn(); 173 | }, 174 | function (): void { 175 | if ($this->isConnected()) { 176 | $this->disconnect("Unable to find a respawn position"); 177 | } 178 | } 179 | ); 180 | } 181 | public function getModifiers() 182 | { 183 | $modifiers = $this->config->getNested('modifiers'); 184 | if ($modifiers !== null) { 185 | return $modifiers; 186 | } else { 187 | return false; 188 | } 189 | } 190 | 191 | public function calcVITBonus(): void 192 | { 193 | $this->vitBonus = (int)round($this->getVIT() * $this->getVITModifier(), 1); 194 | } 195 | 196 | public function getVIT(): int 197 | { 198 | return $this->vit; 199 | } 200 | 201 | public function setVIT(int $vit): void 202 | { 203 | $this->vit = $vit; 204 | $this->calcVITBonus(); 205 | } 206 | 207 | public function getVITModifier(): float 208 | { 209 | return $this->vitModifier; 210 | } 211 | 212 | public function setVITModifier(float $vitModifier): void 213 | { 214 | $this->vitModifier = $vitModifier; 215 | } 216 | 217 | public function calcDEXBonus(): void 218 | { 219 | $this->dexBonus = (int)round($this->getDex() * $this->getDEXModifier(),1); 220 | } 221 | 222 | public function getDEX(): int 223 | { 224 | return $this->dex; 225 | } 226 | 227 | public function setDEX(int $dex): void 228 | { 229 | $this->dex = $dex; 230 | $this->calcDEXBonus(); 231 | $this->applyDexterityBonus(); 232 | } 233 | 234 | public function getDEXModifier(): float 235 | { 236 | return $this->dexModifier; 237 | } 238 | 239 | public function setDEXModifier(float $dexModifier): void 240 | { 241 | $this->dexModifier = $dexModifier; 242 | } 243 | 244 | public function addSkills(): void 245 | { 246 | /* @var BaseSkill[] */ 247 | $skills = [ 248 | new Coinflip($this), 249 | new DoubleStrike($this), 250 | new Explosion($this), 251 | new Fortress($this), 252 | new HealingAura($this), 253 | new Tank($this) 254 | ]; 255 | foreach ($skills as $skill) { 256 | $this->skills[$skill->getName()] = $skill; 257 | } 258 | } 259 | 260 | public function getSkill(string $skillName): ?BaseSkill 261 | { 262 | return $this->skills[$skillName]; 263 | } 264 | 265 | public function applyDexterityBonus() 266 | { 267 | $dex = $this->getDEXBonus(); 268 | $movement = $this->getAttributeMap()->get(Attribute::MOVEMENT_SPEED); 269 | $movement->setValue($movement->getValue() * (1 + $dex)); 270 | } 271 | 272 | public function getDEXBonus(): float 273 | { 274 | return $this->dexBonus; 275 | } 276 | 277 | public function getMovementSpeed(): float 278 | { 279 | return $this->getAttributeMap()->get(Attribute::MOVEMENT_SPEED)->getValue(); 280 | } 281 | 282 | public function checkSkillLevel() 283 | { 284 | foreach ($this->skills as $skill) { 285 | $skill->checkLevel(); 286 | } 287 | } 288 | 289 | /* @return BaseSkill[] */ 290 | public function getSkills(): array 291 | { 292 | return $this->skills; 293 | } 294 | 295 | public function checkForSkills() 296 | { 297 | foreach ($this->skills as $skill) { 298 | if (!$skill->isUnlocked()) { 299 | $skillBaseUnlock = $skill->getBaseUnlock(); 300 | $met_criteria = 0; 301 | foreach ($skillBaseUnlock as $key => $value) { 302 | 303 | if ($this->getAttribute($key) >= $value) { 304 | $met_criteria++; 305 | } 306 | } 307 | if ($met_criteria == count($skillBaseUnlock)) { 308 | $skill->unlock(); 309 | } 310 | } 311 | } 312 | } 313 | 314 | public function getAttribute(string $attribute): int 315 | { 316 | return $this->getAttributes()[$attribute]; 317 | } 318 | 319 | 320 | #[ArrayShape(['STR' => "int", 'VIT' => "int", 'DEF' => "int", 'DEX' => "int"])] 321 | public function getAttributes(): array 322 | { 323 | return [ 324 | 'STR' => $this->getSTR(), 325 | 'VIT' => $this->getVIT(), 326 | 'DEF' => $this->getDEF(), 327 | 'DEX' => $this->getDEX(), 328 | ]; 329 | } 330 | 331 | public function getSTR(): int 332 | { 333 | return $this->str; 334 | } 335 | 336 | public function setSTR(int $str): void 337 | { 338 | $this->str = $str; 339 | $this->calcSTRBonus(); 340 | } 341 | 342 | public function calcSTRBonus(): void 343 | { 344 | $this->strBonus = (int)round($this->getSTR() * $this->getSTRModifier(), 1); 345 | } 346 | 347 | public function getSTRModifier(): float 348 | { 349 | return $this->strModifier; 350 | } 351 | 352 | public function setSTRModifier(float $strModifier): void 353 | { 354 | $this->strModifier = $strModifier; 355 | } 356 | 357 | public function getDEF(): int 358 | { 359 | return $this->def; 360 | } 361 | 362 | public function setDEF(int $def): void 363 | { 364 | $this->def = $def; 365 | $this->calcDEFBonus(); 366 | } 367 | 368 | public function calcDEFBonus(): void 369 | { 370 | $this->defBonus = (int)round($this->getDEF() * $this->getDEFModifier(),1); 371 | } 372 | 373 | public function getDEFModifier(): float 374 | { 375 | return $this->defModifier; 376 | } 377 | 378 | public function setDEFModifier(float $defModifier) 379 | { 380 | $this->defModifier = $defModifier; 381 | } 382 | 383 | public function resetSkills() 384 | { 385 | foreach ($this->skills as $skill) { 386 | $skill->reset(); 387 | } 388 | } 389 | 390 | public function applyDamageBonus(EntityDamageByEntityEvent $event): void 391 | { 392 | $damager = $event->getDamager(); 393 | $origial_knockback = $event->getKnockBack(); 394 | if ($damager instanceof RPGPlayer) { 395 | $event->setModifier($this->getSTRBonus() + $event->getBaseDamage(), EntityDamageEvent::CAUSE_ENTITY_ATTACK); 396 | } 397 | $event->setKnockBack($origial_knockback); 398 | } 399 | 400 | public function getSTRBonus(): float 401 | { 402 | return $this->strBonus; 403 | } 404 | 405 | public function applyVitalityBonus() 406 | { 407 | $this->setMaxHealth(20 + $this->getVITBonus()); 408 | $this->setHealth(20 + $this->getVITBonus()); 409 | } 410 | 411 | public function getVITBonus(): int 412 | { 413 | return (int)ceil($this->vitBonus); 414 | } 415 | 416 | public function applyDefenseBonus(EntityDamageByEntityEvent $event): void 417 | { 418 | $receiver = $event->getEntity(); 419 | if ($receiver instanceof RPGPlayer) { 420 | $event->setBaseDamage($event->getFinalDamage() - $receiver->getDEFBonus()); 421 | } 422 | } 423 | 424 | public function getDEFBonus(): float 425 | { 426 | return $this->defBonus; 427 | } 428 | 429 | public function getHealthRegenBonus() 430 | { 431 | return floor($this->getVITBonus() / 3); 432 | } 433 | 434 | public function savePlayerVariables(): void 435 | { 436 | $playersConfig = RPGLike::getInstance()->getPlayers(); 437 | $playerVars = [ 438 | 'attributes' => $this->getAttributes(), 439 | 'skills' => $this->getSkillNames(), 440 | 'spleft' => $this->getSPleft(), 441 | 'level' => $this->getXpLvl(), 442 | 'blocks' => $this->getBrokenBlocks() 443 | ]; 444 | $players = $playersConfig->getAll(); 445 | $players[$this->getName()] = $playerVars; 446 | $playersConfig->setAll($players); 447 | $playersConfig->save(); 448 | } 449 | 450 | public function getBrokenBlocks(): array 451 | { 452 | $broken_blocks = []; 453 | foreach ($this->traits as $key => $trait) { 454 | $broken_blocks[$key] = $trait->getBlockBreaks(); 455 | } 456 | return $broken_blocks; 457 | } 458 | 459 | public function restorePlayerVariables() 460 | { 461 | $cachedPlayer = $this->getPlayerVariables(); 462 | if ($cachedPlayer) { 463 | $attributes = $cachedPlayer['attributes']; 464 | $this->setDEF($attributes['DEF']); 465 | $this->setDEX($attributes['DEX']); 466 | $this->setSTR($attributes['STR']); 467 | $this->setVIT($attributes['VIT']); 468 | 469 | $this->xplevel = $cachedPlayer['level']; 470 | 471 | $this->calcDEXBonus(); 472 | $this->calcDEFBonus(); 473 | $this->calcVITBonus(); 474 | $this->calcSTRBonus(); 475 | 476 | $this->applyDexterityBonus(); 477 | $this->applyVitalityBonus(); 478 | 479 | $this->setSPleft($cachedPlayer['spleft']); 480 | if (!empty($cachedPlayer['skills'])) { 481 | foreach ($cachedPlayer['skills'] as $skill) { 482 | $this->getSkill($skill)->unlock(true); 483 | } 484 | } 485 | foreach ($this->skills as $skill) { 486 | $skill->checkLevel(true); 487 | } 488 | foreach ($cachedPlayer['blocks'] as $trait => $block_count) { 489 | $this->traits[$trait]->restorePlayerTrait($block_count); 490 | } 491 | } 492 | } 493 | 494 | public function getPlayerVariables() 495 | { 496 | $players = RPGLike::getInstance()->getPlayers()->getAll(); 497 | if (array_key_exists($this->getName(), $players)) { 498 | return $players[$this->getName()]; 499 | } else { 500 | return false; 501 | } 502 | 503 | } 504 | 505 | /* @return string[] */ 506 | public function getSkillNames(): array 507 | { 508 | $skills = []; 509 | foreach ($this->skills as $skill) { 510 | if ($skill->isUnlocked()) { 511 | $skills[] = $skill->getName(); 512 | } 513 | } 514 | return $skills; 515 | } 516 | 517 | public function getSPleft(): int 518 | { 519 | return $this->spleft; 520 | } 521 | 522 | public function setSPleft(int $spleft) 523 | { 524 | $this->spleft = $spleft; 525 | } 526 | 527 | public function setExperienceLevel(int $level) 528 | { 529 | $this->xplevel = $level; 530 | $this->getXpManager()->setXpLevel($level); 531 | } 532 | 533 | public function getXpLvl(): int 534 | { 535 | return $this->xplevel; 536 | } 537 | 538 | public function getTraits(): array 539 | { 540 | return $this->traits; 541 | } 542 | 543 | public function getConfig(): Config 544 | { 545 | return $this->config; 546 | } 547 | 548 | public function hasPartyInvite() 549 | { 550 | if ($this->invites['party'] != '') { 551 | return true; 552 | } 553 | return false; 554 | } 555 | 556 | public function sendPartyInvite(string $party_key) 557 | { 558 | $this->invites['party'] = $party_key; 559 | $this->sendMessage("You've been invited to join into party: " . $party_key . ". You can accept or decline using /party accept|decline"); 560 | } 561 | 562 | public function removePartyInvite() 563 | { 564 | $this->invites['party'] = ''; 565 | } 566 | 567 | public function getParty() 568 | { 569 | return PartySystem::getPlayerParty($this->partyName); 570 | } 571 | 572 | public function getPartyInvite(): string 573 | { 574 | return $this->invites['party']; 575 | } 576 | 577 | public function shareEXP(PlayerExperienceChangeEvent $event) 578 | { 579 | $party = $this->getParty(); 580 | if ($party) { 581 | $members = $party->getPartyMembers(); 582 | foreach ($members as $member) { 583 | if ($member == $this) { 584 | continue; 585 | } else { 586 | $member->getBonusExp($event->getNewProgress() - $event->getOldProgress()); 587 | } 588 | } 589 | } 590 | } 591 | 592 | public function getBonusExp(float $progress) 593 | { 594 | $this->getXpManager()->setXpProgress($this->getXpManager()->getXpProgress() + $progress);//Not sure if this won't cause any issues 595 | } 596 | } 597 | 598 | --------------------------------------------------------------------------------