├── .gitignore ├── .poggit.yml ├── LICENSE.md ├── README.md ├── plugin.yml ├── resources ├── .version_file ├── BlockPetsLogo.png ├── config.yml ├── database_stmts │ ├── mysql.sql │ └── sqlite.sql ├── languages │ ├── chs.yml │ ├── cht.yml │ ├── de.yml │ ├── en.yml │ ├── gr.yml │ ├── ko.yml │ ├── nl.yml │ ├── ru.yml │ └── vi.yml ├── patches │ ├── mysql.sql │ └── sqlite.sql └── pet_properties.yml └── src └── BlockHorizons └── BlockPets ├── Loader.php ├── commands ├── AddPetPointsCommand.php ├── BaseCommand.php ├── ChangePetNameCommand.php ├── ClearPetCommand.php ├── HealPetCommand.php ├── LevelUpPetCommand.php ├── ListPetsCommand.php ├── PetCommand.php ├── PetsTopCommand.php ├── RemovePetCommand.php ├── SpawnPetCommand.php └── TogglePetCommand.php ├── configurable ├── BlockPetsConfig.php ├── LanguageConfig.php ├── PetMessages.php └── PetProperties.php ├── events ├── BlockPetsEvent.php ├── PetEvent.php ├── PetInventoryInitializationEvent.php ├── PetLevelUpEvent.php ├── PetRemoveEvent.php ├── PetRespawnEvent.php └── PetSpawnEvent.php ├── items └── Saddle.php ├── listeners ├── EventListener.php └── RidingListener.php ├── pets ├── BasePet.php ├── BouncingPet.php ├── Calculator.php ├── HoveringPet.php ├── IrasciblePet.php ├── LevelCalculator.php ├── PetData.php ├── PetIds.php ├── SmallCreature.php ├── SwimmingPet.php ├── WalkingPet.php ├── creatures │ ├── AllayPet.php │ ├── ArrowPet.php │ ├── AxolotlPet.php │ ├── BatPet.php │ ├── BeePet.php │ ├── BlazePet.php │ ├── CaveSpiderPet.php │ ├── ChickenPet.php │ ├── CowPet.php │ ├── CreeperPet.php │ ├── DonkeyPet.php │ ├── ElderGuardianPet.php │ ├── EnderCrystalPet.php │ ├── EnderDragonPet.php │ ├── EndermanPet.php │ ├── EndermitePet.php │ ├── EvokerPet.php │ ├── FoxPet.php │ ├── GhastPet.php │ ├── GoatPet.php │ ├── GuardianPet.php │ ├── HorsePet.php │ ├── HuskPet.php │ ├── IronGolemPet.php │ ├── LlamaPet.php │ ├── MagmaCubePet.php │ ├── MooshroomPet.php │ ├── MulePet.php │ ├── OcelotPet.php │ ├── PigPet.php │ ├── PolarBearPet.php │ ├── RabbitPet.php │ ├── SheepPet.php │ ├── SilverFishPet.php │ ├── SkeletonHorsePet.php │ ├── SkeletonPet.php │ ├── SlimePet.php │ ├── SnowGolemPet.php │ ├── SpiderPet.php │ ├── SquidPet.php │ ├── StrayPet.php │ ├── StriderPet.php │ ├── VexPet.php │ ├── VillagerPet.php │ ├── VindicatorPet.php │ ├── WardenPet.php │ ├── WitchPet.php │ ├── WitherPet.php │ ├── WitherSkeletonPet.php │ ├── WitherSkullPet.php │ ├── WolfPet.php │ ├── ZombieHorsePet.php │ ├── ZombiePet.php │ ├── ZombiePigmanPet.php │ └── ZombieVillagerPet.php ├── datastorage │ ├── BaseDataStorer.php │ ├── MySQLDataStorer.php │ ├── SQLDataStorer.php │ └── SQLiteDataStorer.php └── inventory │ └── PetInventoryManager.php └── tasks ├── BaseTask.php └── PetRespawnTask.php /.gitignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .idea/ 3 | -------------------------------------------------------------------------------- /.poggit.yml: -------------------------------------------------------------------------------- 1 | --- # Poggit-CI Manifest. Open the CI at https://poggit.pmmp.io/ci/BlockHorizons/BlockPets 2 | branches: 3 | - master 4 | projects: 5 | BlockPets: 6 | path: "" 7 | icon: resources/BlockPetsLogo.png 8 | compressBuilds: false 9 | fullGzip: 9 10 | libs: 11 | - src: poggit/libasynql/libasynql 12 | version: ^4.0.1 13 | - src: muqsit/invmenu/invmenu 14 | version: ^4.4.0 15 | ... 16 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2017 BlockHorizons 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlockPets 2 | [![Chat](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/YynM57V) 3 | [![](https://poggit.pmmp.io/shield.dl.total/BlockPets)](https://poggit.pmmp.io/p/BlockPets) 4 | 5 | An advanced pets plugin for PocketMine-MP, a server software for Minecraft PE servers in PHP. Implements highly customizable pets with interesting features.
6 | > Third party versions, forks or spoons of PocketMine-MP are **not** supported. 7 | > 8 | > Issues caused by other server softwares than PocketMine-MP will be closed immediately. 9 | 10 | ### Installation 11 | Stable releases of BlockPets will be drafted at the Release tab with a pre-built phar file, and can be found on the Poggit website once released. Alternatively, you can grab a development build from the Poggit button below. 12 | To install: 13 | - Download one of the pre-built phar files from either releases or the Poggit button below. 14 | - Drop the phar file in the plugins folder of your server. 15 | - Restart the server, and enjoy.

16 | [![Poggit-CI](https://poggit.pmmp.io/ci.shield/BlockHorizons/BlockPets/BlockPets)](https://poggit.pmmp.io/ci/BlockHorizons/BlockPets/BlockPets)
17 | 18 | ### Feedback 19 | We'd like to keep our issue tracker as clean as possible, so please take the following in consideration when opening a new issue in the issue tracker: 20 | - If you have an issue with the plugin, check if the issue exists in the Issue tracker, and report it if not. 21 | - If you'd like support or have a question about the plugin, please click the Gitter button under the title. 22 | - If you have a great idea for a new feature or enhancement, please create a new issue in the Issue tracker. 23 | - If you just want to have a talk, please click the Gitter button below the title. 24 | 25 | ### Wiki 26 | Please refer to [BlockPets' wiki](https://github.com/BlockHorizons/BlockPets/wiki) for help with features, translations, commands and permissions. 27 | -------------------------------------------------------------------------------- /plugin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: BlockPets 3 | main: BlockHorizons\BlockPets\Loader 4 | version: 2.0.2 5 | api: 4.0.0 6 | author: BlockHorizons 7 | # mcpe-protocol: 448 8 | description: A pets plugin for PocketMine-MP 9 | 10 | permissions: 11 | blockpets: 12 | default: false 13 | description: "Allows access to all BlockPets features." 14 | blockpets.bypass-limit: 15 | default: op 16 | description: "Allows bypassing the pet limit set in the config.yml." 17 | blockpets.bypass-size-limit: 18 | default: op 19 | description: "Allows bypassing the maximum size of pets." 20 | blockpets.command: 21 | default: false 22 | description: "Allows access to all BlockPets command features." 23 | blockpets.command.spawnpet: 24 | default: false 25 | description: "Allows access to use the spawnpet command." 26 | blockpets.command.spawnpet.use: 27 | default: op 28 | description: "Allows access to use the spawnpet command for yourself." 29 | blockpets.command.spawnpet.others: 30 | default: op 31 | description: "Allows access to use the spawnpet command for other players." 32 | blockpets.command.changepetname: 33 | default: false 34 | description: "Allows access to use the changepetname command." 35 | blockpets.command.changepetname.use: 36 | default: op 37 | description: "Allows access to use the changepetname command for yourself." 38 | blockpets.command.changepetname.others: 39 | default: op 40 | description: "Allows access to use the changepetname command for others." 41 | blockpets.command.removepet: 42 | default: op 43 | description: "Allows access to use the removepet command." 44 | blockpets.command.leveluppet: 45 | default: op 46 | description: "Allows access to use the leveluppet command." 47 | blockpets.command.addpetpoints: 48 | default: op 49 | description: "Allows access to use the addpetpoints command." 50 | blockpets.command.healpet: 51 | default: op 52 | description: "Allows access to use the healpet command." 53 | blockpets.command.clearpet: 54 | default: true 55 | description: "Allows access to use the clearpet command." 56 | blockpets.command.togglepet: 57 | default: true 58 | description: "Allows access to use the togglepets command." 59 | blockpets.command.pet: 60 | default: op 61 | description: "Allows access to use the pets command." 62 | blockpets.command.listpets: 63 | default: op 64 | description: "Allows access to use the listpets command." 65 | blockpets.command.petstop: 66 | default: op 67 | description: "Allows access to use the petstop command." 68 | blockpets.pet: 69 | default: false 70 | description: "Allows access to all BlockPets pets." 71 | blockpets.pet.arrow: 72 | default: op 73 | description: "Allows access to use the arrow pet." 74 | blockpets.pet.bat: 75 | default: op 76 | description: "Allows access to use the bat pet." 77 | blockpets.pet.blaze: 78 | default: op 79 | description: "Allows access to use the blaze pet." 80 | blockpets.pet.cavespider: 81 | default: op 82 | description: "Allows access to use the cave spider pet." 83 | blockpets.pet.chicken: 84 | default: op 85 | description: "Allows access to use the chicken pet." 86 | blockpets.pet.cow: 87 | default: op 88 | description: "Allows access to use the cow pet." 89 | blockpets.pet.creeper: 90 | default: op 91 | description: "Allows access to use the creeper pet." 92 | blockpets.pet.donkey: 93 | default: op 94 | description: "Allows access to use the donkey pet." 95 | blockpets.pet.elderguardian: 96 | default: op 97 | description: "Allows access to use the elder guardian pet." 98 | blockpets.pet.endercrystal: 99 | default: op 100 | description: "Allows access to use the ender crystal pet." 101 | blockpets.pet.enderdragon: 102 | default: op 103 | description: "Allows access to use the ender dragon pet." 104 | blockpets.pet.enderman: 105 | default: op 106 | description: "Allows access to use the enderman pet." 107 | blockpets.pet.endermite: 108 | default: op 109 | description: "Allows access to use the endermite pet." 110 | blockpets.pet.evocationfangs: 111 | default: op 112 | description: "Allows access to use the evocation fangs pet." 113 | blockpets.pet.evoker: 114 | default: op 115 | description: "Allows access to use the evoker pet." 116 | blockpets.pet.ghast: 117 | default: op 118 | description: "Allows access to use the ghast pet." 119 | blockpets.pet.guardian: 120 | default: op 121 | description: "Allows access to use the guardian pet." 122 | blockpets.pet.horse: 123 | default: op 124 | description: "Allows access to use the horse pet." 125 | blockpets.pet.husk: 126 | default: op 127 | description: "Allows access to use the husk pet." 128 | blockpets.pet.irongolem: 129 | default: op 130 | description: "Allows access to use the iron golem pet." 131 | blockpets.pet.llama: 132 | default: op 133 | description: "Allows access to use the llama pet." 134 | blockpets.pet.magmacube: 135 | default: op 136 | description: "Allows access to use the magma cube pet." 137 | blockpets.pet.mooshroom: 138 | default: op 139 | description: "Allows access to use the mooshroom pet." 140 | blockpets.pet.mule: 141 | default: op 142 | description: "Allows access to use the mule pet." 143 | blockpets.pet.ocelot: 144 | default: op 145 | description: "Allows access to use the ocelot pet." 146 | blockpets.pet.pig: 147 | default: op 148 | description: "Allows access to use the pig pet." 149 | blockpets.pet.polarbear: 150 | default: op 151 | description: "Allows access to use the polar bear pet." 152 | blockpets.pet.rabbit: 153 | default: op 154 | description: "Allows access to use the rabbit pet." 155 | blockpets.pet.sheep: 156 | default: op 157 | description: "Allows access to use the sheep pet." 158 | blockpets.pet.silverfish: 159 | default: op 160 | description: "Allows access to use the silverfish pet." 161 | blockpets.pet.skeleton: 162 | default: op 163 | description: "Allows access to use the skeleton pet." 164 | blockpets.pet.skeletonhorse: 165 | default: op 166 | description: "Allows access to use the skeleton horse pet." 167 | blockpets.pet.slime: 168 | default: op 169 | description: "Allows access to use the slime pet." 170 | blockpets.pet.snowgolem: 171 | default: op 172 | description: "Allows access to use the snow golem pet." 173 | blockpets.pet.spider: 174 | default: op 175 | description: "Allows access to use the spider pet." 176 | blockpets.pet.squid: 177 | default: op 178 | description: "Allows access to use the squid pet." 179 | blockpets.pet.stray: 180 | default: op 181 | description: "Allows access to use the stray pet." 182 | blockpets.pet.strider: 183 | default: op 184 | description: "Allows access to use the strider pet." 185 | blockpets.pet.vex: 186 | default: op 187 | description: "Allows access to use the vex pet." 188 | blockpets.pet.villager: 189 | default: op 190 | description: "Allows access to use the village pet." 191 | blockpets.pet.vindicator: 192 | default: op 193 | description: "Allows access to use the vindicator pet." 194 | blockpets.pet.witch: 195 | default: op 196 | description: "Allows access to use the witch pet." 197 | blockpets.pet.wither: 198 | default: op 199 | description: "Allows access to use the wither pet." 200 | blockpets.pet.witherskeleton: 201 | default: op 202 | description: "Allows access to use the wither skeleton pet." 203 | blockpets.pet.witherskull: 204 | default: op 205 | description: "Allows access to use the wither skull pet." 206 | blockpets.pet.wolf: 207 | default: op 208 | description: "Allows access to use the wolf pet." 209 | blockpets.pet.zombie: 210 | default: op 211 | description: "Allows access to use the zombie pet." 212 | blockpets.pet.zombiehorse: 213 | default: op 214 | description: "Allows access to use the zombie horse pet." 215 | blockpets.pet.zombiepigman: 216 | default: op 217 | description: "Allows access to use the zombie pigman pet." 218 | blockpets.pet.zombievillager: 219 | default: op 220 | description: "Allows access to use the zombie villager pet." 221 | ... 222 | -------------------------------------------------------------------------------- /resources/.version_file: -------------------------------------------------------------------------------- 1 | # DO NOT MODIFY THE CONTENTS OF THIS FILE... 2 | # unless you want to break this plugin's versioning system, then you may do so. 3 | version: 1.1.2 -------------------------------------------------------------------------------- /resources/BlockPetsLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BlockHorizons/BlockPets/8b1adb7c6c650ece6c7b896f3b3111772088e9d1/resources/BlockPetsLogo.png -------------------------------------------------------------------------------- /resources/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | # The language to be used in messages in this plugin. 5 | # Available options: 6 | # - en (English) 7 | # - nl (Dutch) 8 | # - vi (Vietnamese) 9 | # - gr (Greek) 10 | # - ko (Korean) 11 | # - cht (繁體中文) 12 | # - chs (简体中文) 13 | Language: en 14 | 15 | # Whether to make pets invulnerable to any damage, or allow damaging them. 16 | Invulnerable-Pets: false 17 | 18 | # Whether to directly follow the player, or search random positions near the player. 19 | Stalk-Owner-Blindly: false 20 | 21 | # Whether to make the pet invulnerable to damage if the owner of it is or not. 22 | Invulnerable-If-Owner-Is: true 23 | 24 | # The time in seconds to wait before spawning a pet that died earlier. 25 | Pet-Respawn-Time: 10 26 | 27 | # Whether to make pets attack the person that attacked them or their owner. 28 | Attacking-Pets: true 29 | 30 | # The limit of pets per player by default. 31 | Pet-Limit: 3 32 | 33 | # The base health a pet will be spawned with. This is equal to the health of a pet with level 0. 34 | Pet-Base-Health: 20 35 | 36 | # The health that gets added each level the pet gets. 37 | Pet-Per-Level-Health: 0.5 38 | 39 | # The size that gets added each level the pet gets. 40 | Pet-Per-Level-Size: 0.02 41 | 42 | # The base damage a pet will be spawned with. This is equal to the damage of a pet with level 0. 43 | Pet-Base-Damage: 4 44 | 45 | # The damage that gets added each level the pet gets. 46 | Pet-Per-Level-Damage: 0.1 47 | 48 | # The experience a pet gets each time it kills a player. 49 | Experience-Points-Per-Player-Kill: 15 50 | 51 | # The experience a pet gets each time it kills an entity. 52 | Experience-Points-Per-Entity-Kill: 10 53 | 54 | # Whether to give the pet some experience if it's being fed by the owner or not. 55 | Give-Experience-For-Feeding: true 56 | 57 | # The database used to store data of pets in. This is only relevant if storage to database is turned to true. 58 | # Options: 59 | # - MySQL 60 | # - SQLite 61 | Database: SQLite 62 | 63 | # MySQL information. This is only relevant if database above is set to MySQL. 64 | MySQL-Info: 65 | Host: 127.0.0.1 66 | User: Admin 67 | Password: Admin 68 | Database: BlockPets 69 | Port: 3306 70 | 71 | # Whether to store pet data to the database set above or not. 72 | Store-To-Database: true 73 | 74 | # WARNING: This will delete all pets and pet data the next restart if set to true. 75 | # Use with caution! 76 | Hard-Reset: false 77 | ... 78 | -------------------------------------------------------------------------------- /resources/database_stmts/mysql.sql: -------------------------------------------------------------------------------- 1 | -- #!mysql 2 | -- #{ blockpets 3 | 4 | -- # { init 5 | CREATE TABLE IF NOT EXISTS Pets( 6 | Player VARCHAR(16) NOT NULL, 7 | PetName VARCHAR(48) NOT NULL, 8 | EntityName VARCHAR(32) NOT NULL, 9 | PetSize FLOAT NOT NULL DEFAULT 1.0, 10 | IsBaby BOOL NOT NULL DEFAULT false, 11 | Chested BOOL NOT NULL DEFAULT false, 12 | Visible BOOL NOT NULL DEFAULT true, 13 | PetLevel INT UNSIGNED NOT NULL DEFAULT 1, 14 | LevelPoints INT UNSIGNED NOT NULL DEFAULT 0, 15 | Inventory BLOB, 16 | PRIMARY KEY(Player, PetName) 17 | ); 18 | -- # } 19 | 20 | -- # { loadplayer 21 | -- # :player string 22 | SELECT 23 | PetName, 24 | EntityName, 25 | PetSize, 26 | IsBaby, 27 | Chested, 28 | PetLevel, 29 | LevelPoints, 30 | Visible, 31 | Inventory 32 | FROM Pets WHERE Player=:player; 33 | -- # } 34 | 35 | -- # { listpets 36 | -- # :player string 37 | -- # :entityname string 38 | SELECT 39 | PetName, 40 | EntityName, 41 | Visible 42 | FROM Pets WHERE Player=:player AND EntityName LIKE :entityname; 43 | -- # } 44 | 45 | -- # { reset 46 | DELETE FROM Pets; 47 | -- # } 48 | 49 | -- # { pet 50 | 51 | -- # { register 52 | -- # :player string 53 | -- # :petname string 54 | -- # :entityname string 55 | -- # :petsize float 56 | -- # :isbaby int 57 | -- # :chested int 58 | -- # :petlevel int 59 | -- # :levelpoints int 60 | INSERT INTO Pets( 61 | Player, 62 | PetName, 63 | EntityName, 64 | PetSize, 65 | IsBaby, 66 | Chested, 67 | PetLevel, 68 | LevelPoints 69 | ) VALUES ( 70 | :player, 71 | :petname, 72 | :entityname, 73 | :petsize, 74 | :isbaby, 75 | :chested, 76 | :petlevel, 77 | :levelpoints 78 | ) ON DUPLICATE KEY UPDATE 79 | PetName=VALUES(PetName), 80 | EntityName=VALUES(EntityName), 81 | PetSize=VALUES(PetSize), 82 | IsBaby=VALUES(IsBaby), 83 | Chested=VALUES(Chested), 84 | PetLevel=VALUES(PetLevel), 85 | LevelPoints=VALUES(LevelPoints); 86 | -- # } 87 | 88 | -- # { unregister 89 | -- # :player string 90 | -- # :petname string 91 | DELETE FROM Pets 92 | WHERE Player=:player AND petname=:petname; 93 | -- # } 94 | 95 | -- # { leaderboard 96 | -- # :offset int 97 | -- # :length int 98 | -- # :entityname string 99 | SELECT 100 | Player, 101 | PetName, 102 | EntityName, 103 | PetLevel, 104 | LevelPoints 105 | FROM Pets WHERE EntityName LIKE :entityname ORDER BY LevelPoints, PetLevel DESC LIMIT :offset, :length; 106 | -- # } 107 | 108 | -- # { visibility 109 | -- # { toggle 110 | -- # :player string 111 | -- # :petname string 112 | UPDATE Pets SET Visible=NOT Visible 113 | WHERE Player=:player AND PetName LIKE :petname; 114 | -- # } 115 | -- # { select 116 | -- # :player string 117 | -- # :petname string 118 | SELECT PetName, Visible FROM Pets 119 | WHERE Player=:player AND PetName LIKE :petname; 120 | -- # } 121 | -- # } 122 | 123 | -- # { update 124 | -- # { chested 125 | -- # :chested int 126 | -- # :player string 127 | -- # :petname string 128 | UPDATE Pets SET 129 | Chested=:chested 130 | WHERE Player=:player AND PetName=:petname; 131 | -- # } 132 | -- # { exp 133 | -- # :petlevel int 134 | -- # :levelpoints int 135 | -- # :player string 136 | -- # :petname string 137 | UPDATE Pets SET 138 | PetLevel=:petlevel, 139 | LevelPoints=:levelpoints 140 | WHERE Player=:player AND PetName=:petname; 141 | -- # } 142 | -- # { inv 143 | -- # :inventory string 144 | -- # :player string 145 | -- # :petname string 146 | UPDATE Pets SET 147 | Inventory=:inventory 148 | WHERE Player=:player AND PetName=:petname; 149 | -- # } 150 | -- # } 151 | -- # } 152 | 153 | -- #} 154 | -------------------------------------------------------------------------------- /resources/database_stmts/sqlite.sql: -------------------------------------------------------------------------------- 1 | -- #!mysql 2 | -- #{ blockpets 3 | 4 | -- # { init 5 | CREATE TABLE IF NOT EXISTS Pets( 6 | Player VARCHAR(16) NOT NULL, 7 | PetName VARCHAR(48) NOT NULL, 8 | EntityName VARCHAR(32) NOT NULL, 9 | PetSize FLOAT NOT NULL DEFAULT 1.0, 10 | IsBaby BOOL NOT NULL DEFAULT false, 11 | Chested BOOL NOT NULL DEFAULT false, 12 | Visible BOOL NOT NULL DEFAULT true, 13 | PetLevel INT UNSIGNED NOT NULL DEFAULT 1, 14 | LevelPoints INT UNSIGNED NOT NULL DEFAULT 0, 15 | Inventory BLOB, 16 | PRIMARY KEY(Player, PetName) 17 | ); 18 | -- # } 19 | 20 | -- # { loadplayer 21 | -- # :player string 22 | SELECT 23 | PetName, 24 | EntityName, 25 | PetSize, 26 | IsBaby, 27 | Chested, 28 | PetLevel, 29 | LevelPoints, 30 | Visible, 31 | Inventory 32 | FROM Pets WHERE Player=:player; 33 | -- # } 34 | 35 | -- # { listpets 36 | -- # :player string 37 | -- # :entityname string 38 | SELECT 39 | PetName, 40 | EntityName, 41 | Visible 42 | FROM Pets WHERE Player=:player AND EntityName LIKE :entityname; 43 | -- # } 44 | 45 | -- # { reset 46 | DELETE FROM Pets; 47 | -- # } 48 | 49 | -- # { pet 50 | 51 | -- # { register 52 | -- # :player string 53 | -- # :petname string 54 | -- # :entityname string 55 | -- # :petsize float 56 | -- # :isbaby int 57 | -- # :chested int 58 | -- # :petlevel int 59 | -- # :levelpoints int 60 | INSERT OR REPLACE INTO Pets( 61 | Player, 62 | PetName, 63 | EntityName, 64 | PetSize, 65 | IsBaby, 66 | Chested, 67 | PetLevel, 68 | LevelPoints 69 | ) VALUES ( 70 | :player, 71 | :petname, 72 | :entityname, 73 | :petsize, 74 | :isbaby, 75 | :chested, 76 | :petlevel, 77 | :levelpoints 78 | ); 79 | -- # } 80 | 81 | -- # { unregister 82 | -- # :player string 83 | -- # :petname string 84 | DELETE FROM Pets 85 | WHERE Player=:player AND petname=:petname; 86 | -- # } 87 | 88 | -- # { leaderboard 89 | -- # :offset int 90 | -- # :length int 91 | -- # :entityname string 92 | SELECT 93 | Player, 94 | PetName, 95 | EntityName, 96 | PetLevel, 97 | LevelPoints 98 | FROM Pets WHERE EntityName LIKE :entityname ORDER BY LevelPoints, PetLevel DESC LIMIT :offset, :length; 99 | -- # } 100 | 101 | -- # { visibility 102 | -- # { toggle 103 | -- # :player string 104 | -- # :petname string 105 | UPDATE Pets SET Visible=NOT Visible 106 | WHERE Player=:player AND PetName LIKE :petname; 107 | -- # } 108 | -- # { select 109 | -- # :player string 110 | -- # :petname string 111 | SELECT PetName, Visible FROM Pets 112 | WHERE Player=:player AND PetName LIKE :petname; 113 | -- # } 114 | -- # } 115 | 116 | -- # { update 117 | -- # { chested 118 | -- # :chested int 119 | -- # :player string 120 | -- # :petname string 121 | UPDATE Pets SET 122 | Chested=:chested 123 | WHERE Player=:player AND PetName=:petname; 124 | -- # } 125 | -- # { exp 126 | -- # :petlevel int 127 | -- # :levelpoints int 128 | -- # :player string 129 | -- # :petname string 130 | UPDATE Pets SET 131 | PetLevel=:petlevel, 132 | LevelPoints=:levelpoints 133 | WHERE Player=:player AND PetName=:petname; 134 | -- # } 135 | -- # { inv 136 | -- # :inventory string 137 | -- # :player string 138 | -- # :petname string 139 | UPDATE Pets SET 140 | Inventory=:inventory 141 | WHERE Player=:player AND PetName=:petname; 142 | -- # } 143 | -- # } 144 | -- # } 145 | 146 | -- #} 147 | -------------------------------------------------------------------------------- /resources/languages/chs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Simplified Chinese messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6警告&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "当您从控制台只用该命令时,务必填写所有参数" 10 | no-console-use: "该命令仅可由玩家使用" 11 | no-permission: "您并无权限使用该命令" 12 | plugin-cancelled: "插件操作已取消" 13 | 14 | pet: 15 | doesnt-exist: "该名称宠物不存在" 16 | numeric: "宠物的大小必须是一个数值" 17 | 18 | player: 19 | not-found: "无法找到该玩家" 20 | no-pet: "您不能拥有叫这个名称的宠物" 21 | no-pet-other: "该玩家不能拥有叫这个名称的宠物" 22 | already-own-pet: "您已拥有叫这个名称的宠物了" 23 | 24 | changepetname: 25 | no-permission: "您并无权限更改其他玩家的宠物名称" 26 | success: "成功从 %s &r&a 更改名称为 %s!" 27 | 28 | clearpet: 29 | success: "成功清除了宠物 &b%s&a!" 30 | 31 | healpet: 32 | success: "%s &r&a 已成功被治疗!" 33 | 34 | leveluppet: 35 | spawnpet: 36 | success: "宠物已升等: &b%s &r&a %s!" 37 | 38 | no-permission: "您并无权限生成宠物" 39 | no-permission-others: "您并无权限为其他人生成宠物" 40 | success: "成功生成一隻名为 &b%s&a 的宠物!" 41 | success-other: "您收到了一隻名为 &b%s&a 的宠物!" 42 | name: "请于聊天窗中为您宠物取一个名字!" 43 | selecting-name: "%s 正在为他们的宠物取一个名字!" 44 | exceeded-limit: "%s 超出宠物的限制" 45 | 46 | removepet: 47 | success: "成功移除宠物 &b%s&a!" 48 | 49 | togglepet: 50 | no-pet-specified: "您并无指定要切换的宠物" 51 | success: "成功切换了您的宠物 %s!" 52 | success-others: "成功切换宠物 &b%s &r&a %s!" 53 | ... 54 | -------------------------------------------------------------------------------- /resources/languages/cht.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Traditional Chinese messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6警告&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "當您從控制台只用該指令時,務必填寫所有參數" 10 | no-console-use: "該指令僅可由玩家使用" 11 | no-permission: "您並無權限使用該指令" 12 | plugin-cancelled: "插件操作已取消" 13 | 14 | pet: 15 | doesnt-exist: "該名稱寵物不存在" 16 | numeric: "寵物的大小必須是一個數值" 17 | 18 | player: 19 | not-found: "無法找到該玩家" 20 | no-pet: "您不能擁有叫這個名稱的寵物" 21 | no-pet-other: "該玩家不能擁有叫這個名稱的寵物" 22 | already-own-pet: "您已擁有叫這個名稱的寵物了" 23 | 24 | changepetname: 25 | no-permission: "您並無權限更改其他玩家的寵物名稱" 26 | success: "成功從 %s &r&a 更改名稱為 %s!" 27 | 28 | clearpet: 29 | success: "成功清除了寵物 &b%s&a!" 30 | 31 | healpet: 32 | success: "%s &r&a 已成功被治療!" 33 | 34 | leveluppet: 35 | spawnpet: 36 | success: "寵物已升等: &b%s &r&a %s!" 37 | 38 | no-permission: "您並無權限生成寵物" 39 | no-permission-others: "您並無權限為其他人生成寵物" 40 | success: "成功生成一隻名為 &b%s&a 的寵物!" 41 | success-other: "您收到了一隻名為 &b%s&a 的寵物!" 42 | name: "請於聊天窗中為您寵物取一個名字!" 43 | selecting-name: "%s 正在為他們的寵物取一個名字!" 44 | exceeded-limit: "%s 超出寵物的限制" 45 | 46 | removepet: 47 | success: "成功刪除寵物 &b%s&a!" 48 | 49 | togglepet: 50 | no-pet-specified: "您並無指定要切換的寵物" 51 | success: "成功切換了您的寵物 %s!" 52 | success-others: "成功切換寵物 &b%s &r&a %s!" 53 | ... 54 | -------------------------------------------------------------------------------- /resources/languages/de.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # German messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6Warnung&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "Wenn Sie diesen Befehl von der Konsole aus verwenden, müssen alle Argumente angegeben werden." 10 | no-console-use: "Dieses Kommando können nur Spieler benutzen." 11 | no-permission: "Du hast keine Berechtigung dieses Kommando zu benutzen." 12 | plugin-cancelled: "Ein Plugin hat diese Aktion unterbrochen." 13 | 14 | pet: 15 | doesnt-exist: "Ein Haustier mit diesem Namen gibt es nicht." 16 | numeric: "Die Haustiergröße sollte in Zahlen angegeben werden." 17 | 18 | player: 19 | not-found: "Dieser Spieler konnte nicht gefunden werden." 20 | no-pet: "Du besitzt kein Haustier mit diesem Namen." 21 | no-pet-other: "Dieser Spieler besitzt kein Haustier mit diesem Namen." 22 | already-own-pet: "Du besitzt bereits ein Haustier mit diesem Namen." 23 | 24 | changepetname: 25 | no-permission: "Du hast nicht die Berechtigung um die Namen der Haustiere von anderen zu ändern." 26 | success: "Der Name des Haustieres wurde von %s &r&azu %s geändert!" 27 | 28 | clearpet: 29 | success: "Du hast das Haustier &b%s&a erfolgreich entfernt!" 30 | 31 | healpet: 32 | success: "Das Haustier mit dem Namen %s &r&a wurde geheilt!" 33 | 34 | leveluppet: 35 | success: "Haustierlevel erfolgreich erhöht: &b%s &r&a %s!" 36 | 37 | spawnpet: 38 | no-permission: "Du hast keine Berechtigung dieses Haustier zu erstellen." 39 | no-permission-others: "Du hast keine Berechtigung um anderen Haustiere zu erstellen." 40 | success: "Du hast ein Haustier mit dem Namen &b%s&a erstellt!" 41 | success-other: "Du hast ein Haustier mit dem Namen &b%s&a bekommen!" 42 | name: "Bitte gebe einen Namen für dein Haustier in den Chat ein!" 43 | selecting-name: "%s wählt nun einen Namen für sein/ihr Haustier!" 44 | exceeded-limit: "%s hat das Haustierlimit erreicht." 45 | 46 | removepet: 47 | success: "Das Tier &b%s&a wurde erfolgreich gelöscht!" 48 | 49 | togglepet: 50 | no-pet-specified: "Du hast kein Tier angegeben." 51 | success: "Dein Haustier wurde erfolgreich auf %s umgestellt!" 52 | success-others: "Das Haustier &b%s &r&awurde erfolgreich auf %s gestellt!" 53 | ... 54 | -------------------------------------------------------------------------------- /resources/languages/en.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # English messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6Warning&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "When using that command from the console, all arguments must be provided." 10 | no-console-use: "That command can only be used by players." 11 | no-permission: "You don't have permission to use that command." 12 | plugin-cancelled: "A plugin has cancelled that action." 13 | 14 | pet: 15 | doesnt-exist: "A pet with the given name doesn't exist." 16 | numeric: "The pet scale should be numeric." 17 | none-on-server: "There are no pets on this server." 18 | none-on-server-type: "There are no %s pets on this server." 19 | 20 | player: 21 | not-found: "The given player cannot be found." 22 | no-pet: "You do not own a pet with the given name." 23 | no-pet-other: "The given player does not own a pet with that name." 24 | already-own-pet: "You already own a pet with that name." 25 | 26 | addpetpoints: 27 | success: "Successfully added &b%sxp&a to the pet &b%s&r&a!" 28 | 29 | changepetname: 30 | no-permission: "You don't have permission to change the name of pets from other players." 31 | success: "Successfully changed the name of %s &r&ato %s!" 32 | 33 | clearpet: 34 | success: "Successfully cleared the pet &b%s&a!" 35 | 36 | healpet: 37 | success: "The pet %s &r&a has been healed successfully!" 38 | 39 | leveluppet: 40 | success: "Successfully leveled up the pet: &b%s &r&a %s!" 41 | 42 | listpets: 43 | no-pets-found: "&cYou do not have any pets." 44 | no-pets-found-type: "&cYou do not have any %ss." 45 | 46 | spawnpet: 47 | no-permission: "You don't have permission to spawn that pet." 48 | no-permission-others: "You don't have permission to spawn pets to others." 49 | success: "Successfully spawned a pet with the name &b%s&a!" 50 | success-other: "You have received a pet with the name &b%s&a!" 51 | name: "Please type a name for your pet in chat!" 52 | selecting-name: "%s is now selecting a name for their pet!" 53 | exceeded-limit: "%s exceeded the pet limit." 54 | 55 | removepet: 56 | success: "Successfully removed the pet &b%s&a!" 57 | 58 | togglepet: 59 | no-pet-specified: "You did not specify a pet to toggle." 60 | success: "Successfully toggled your pets %s!" 61 | success-others: "Successfully toggle the pet &b%s &r&a %s!" 62 | success-specific: "Successfully toggled your pet %s %s." 63 | success-specific-others: "Successfully toggled %s's pet %s." 64 | success-diff: "Successfully toggled your pets, visible pets: &b%s." 65 | success-diff-others: "Successfully toggled %s's pets, visible pets: &b%s." 66 | ... 67 | -------------------------------------------------------------------------------- /resources/languages/gr.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Greek messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6Προσοχή&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "Όταν χρησιμοποιείτε αυτήν την εντολή από την κονσόλα, πρέπει να παρέχονται όλα τα δικαιώματα." 10 | no-console-use: "Αυτή η εντολή μπορεί να χρησιμοποιηθεί μόνο από παίκτες." 11 | no-permission: "Δεν έχετε την άδεια να χρησιμοποιήσετε αυτήν την εντολή." 12 | plugin-cancelled: "Ένα άλλο πρόσθετο/plugin ακύρωσε αυτή την ενέργεια." 13 | 14 | pet: 15 | doesnt-exist: "Ένα κατοικίδιο με το συγκεκριμένο όνομα δεν υπάρχει." 16 | numeric: "Η κλίμακα των κατοικίδιων πρέπει να είναι αριθμητική." 17 | 18 | player: 19 | not-found: "Αυτός ο παίκτης δεν βρέθηκε." 20 | no-pet: "Δεν έχετε ένα κατοικίδιο με αυτό το όνομα." 21 | no-pet-other: "Αυτός ο παίκτης δεν έχει ένα κατοικίδιο με αυτό το όνομα." 22 | already-own-pet: "Έχετε ήδη ένα κατοικίδιο με αυτό το όνομα." 23 | 24 | changepetname: 25 | no-permission: "Δεν έχετε άδεια να αλλάξετε το όνομα των κατοικίδιων από άλλους παίκτες." 26 | success: "Αλάξατε με επιτυχία το όνομα του %s &r&aσε %s!" 27 | 28 | clearpet: 29 | success: "Επιτυχής εκκαθάριση του κατοικίδιου &b%s&a!" 30 | 31 | healpet: 32 | success: "Το κατοικίδιο %s &r&a έχει θεραπευτεί με επιτυχία!" 33 | 34 | leveluppet: 35 | success: "Με επιτυχία ανέβηκε επίπεδο το κατοικίδιο: &b%s &r&a %s!" 36 | 37 | spawnpet: 38 | no-permission: "Δεν έχετε την άδεια να δημιουργήσετε αυτό το κατοικίδιο." 39 | no-permission-others: "Δεν έχετε την άδεια να δημιουργήσετε κατοικίδια σε άλλους." 40 | success: "Δημιουργήσατε επιτυχώς ένα κατοικίδιο με το όνομα &b%s&a!" 41 | success-other: "Έχετε λάβει ένα κατοικίδιο με το όνομα &b%s&a!" 42 | name: "Πληκτρολογήστε ένα όνομα για το κατοικίδιο σας στο chat!" 43 | selecting-name: "Ο/Η %s τώρα επιλέγει ένα όνομα για το κατοικίδιο του!" 44 | exceeded-limit: "Ο/Η %s υπερέβη το όριο κατοικίδιων." 45 | 46 | removepet: 47 | success: "Διαγράψατε με επιτυχία το κατοικίδιο &b%s&a!" 48 | 49 | togglepet: 50 | no-pet-specified: "Δεν έχετε ορίσει ένα κατοικίδιο για να αλλάξετε." 51 | success: "Αλλάξατε με επιτυχία τα κατοικίδια σας %s!" 52 | success-others: "Επιτυχώς αλλάξατε το κατοικίδιο &b%s &r&a %s!" 53 | ... 54 | -------------------------------------------------------------------------------- /resources/languages/ko.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Korean messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6경고&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "콘솔에서 해당 명령어를 사용할 때는 모든 인수가 제공되어야 합니다." 10 | no-console-use: "해당 명령어는 플레이어만 사용할 수 있습니다." 11 | no-permission: "해당 명령어를 사용할 수 있는 권한이 없습니다." 12 | plugin-cancelled: "플러그인이 해당 작업을 취소했습니다." 13 | 14 | pet: 15 | doesnt-exist: "주어진 이름의 애완동물은 없습니다." 16 | numeric: "애완동물 규모는 숫자여야 합니다." 17 | none-on-server: "이 서버에는 애완동물이 없습니다." 18 | none-on-server-type: "이 서버에는 %s 애완동물이 없습니다." 19 | 20 | player: 21 | not-found: "주어진 플레이어를 찾을 수 없습니다." 22 | no-pet: "주어진 이름의 애완동물을 가지고 있지 않습니다." 23 | no-pet-other: "주어진 플레이어는 해당 이름의 애완동물을 가지고 있지 않습니다." 24 | already-own-pet: "이미 해당 이름의 애완동물을 가지고 있습니다." 25 | 26 | addpetpoints: 27 | success: "성공적으로 애완동물 &b%2$s&r&a에게 &b%1$s 경험치&a를 추가했습니다!" 28 | 29 | changepetname: 30 | no-permission: "다른 플레이어의 애완동물의 이름을 변경할 수 있는 권한이 없습니다." 31 | success: "%s의 이름&r&a을 %s(으)로 성공적으로 변경했습니다!" 32 | 33 | clearpet: 34 | success: "애완동물 &b%s&a을(를) 성공적으로 제거했습니다!" 35 | 36 | healpet: 37 | success: "애완동물 %s&r&a이(가) 성공적으로 치유되었습니다!" 38 | 39 | leveluppet: 40 | success: "애완동물을 성공적으로 레벨업했습니다: &b%s &r&a %s!" 41 | 42 | listpets: 43 | no-pets-found: "&c애완동물을 가지고 있지 않습니다." 44 | no-pets-found-type: "&c%s을(를) 가지고 있지 않습니다." 45 | 46 | spawnpet: 47 | no-permission: "해당 애완동물을 생성할 권한이 없습니다." 48 | no-permission-others: "다른 플레이어에게 애완동물을 생성할 권한이 없습니다." 49 | success: "이름 &b%s&a의 애완동물을 성공적으로 생성했습니다!" 50 | success-other: "이름 &b%s&a의 애완동물을 받았습니다!" 51 | name: "애완동물의 이름을 대화에 입력해주세요!" 52 | selecting-name: "%s이(가) 애완동물의 이름을 선택하고 있습니다!" 53 | exceeded-limit: "%s이(가) 애완동물 제한을 넘었습니다." 54 | 55 | removepet: 56 | success: "애완동물 &b%s&a을(를) 성공적으로 제거했습니다!" 57 | 58 | togglepet: 59 | no-pet-specified: "전환할 애완동물을 선택하지 않았습니다." 60 | success: "애완동물을 %s(으)로 성공적으로 전환했습니다!" 61 | success-others: "애완동물 &b%s&r&a을(를) %s(으)로 성공적으로 전환했습니다!" 62 | success-specific: "애완동물 %s을(를) %s(으)로 성공적으로 전환했습니다." 63 | success-specific-others: "%s의 애완동물을 %s(으)로 성공적으로 전환했습니다." 64 | success-diff: "애완동물을 성공적으로 전환했습니다, 보이는 애완동물: &b%s." 65 | success-diff-others: "%s의 애완동물을 성공적으로 전환했습니다, 보이는 애완동물: &b%s." 66 | ... 67 | -------------------------------------------------------------------------------- /resources/languages/nl.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Dutch messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6Waarschuwing&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "Wanneer u die opdracht vanuit de console gebruikt, moeten alle argumenten worden opgegeven." 10 | no-console-use: "Dat commando kan alleen gebruikt worden door spelers." 11 | no-permission: "Je hebt geen permissie om dat commando uit te voeren." 12 | plugin-cancelled: "Een andere plugin heeft die actie onderbroken." 13 | 14 | pet: 15 | doesnt-exist: "Een huisdier type met die naam bestaat niet." 16 | numeric: "De huisdier grootte moet numeriek zijn." 17 | 18 | player: 19 | not-found: "De gegeven speler kon niet gevonden worden." 20 | no-pet: "Je beschikt niet over een huisdier met die naam." 21 | no-pet-other: "De gegeven speler beschikt niet over een huisdier met die naam." 22 | already-own-pet: "Je hebt al een huisdier met die naam." 23 | 24 | changepetname: 25 | no-permission: "Je hebt geen permissie om de naam van huisdieren van andere spelers te veranderen." 26 | success: "Naam van %s &r&asuccesvol veranderd naar %s!" 27 | 28 | clearpet: 29 | success: "Huisdier &b%s&a verwijderen succesvol!" 30 | 31 | healpet: 32 | success: "Het huisdier %s &r&a genezen was succesvol!" 33 | 34 | leveluppet: 35 | success: "Huisdier niveau verhogen succesvol: &b%s &r&a %s!" 36 | 37 | spawnpet: 38 | no-permission: "Je hebt geen permissie om dat huisdier te gebruiken." 39 | no-permission-others: "Je hebt geen permissie om ander spelers huisdieren te geven." 40 | success: "Verschijnen van het huisdier &b%s &awas succesvol!" 41 | success-other: "Je hebt een huisdier gekregen met de naam: &b%s&a!" 42 | name: "Type alsjeblieft een naam voor je huisdier!" 43 | selecting-name: "%s is nu een naam voor zijn huisdier aan het kiezen!" 44 | exceeded-limit: "%s heeft de huisdier limiet overschreden." 45 | 46 | removepet: 47 | success: "Verwijderen van het huisdier &b%s &awas succesvol." 48 | 49 | togglepet: 50 | no-pet-specified: "Je hebt geen huisdier gekozen om aan/uit te zetten." 51 | success: "Je hebt je huisdieren %s gezet!" 52 | success-others: "Je hebt het huisdier &b%s &r&a %s gezet!" 53 | ... -------------------------------------------------------------------------------- /resources/languages/ru.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Russian messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6Предупреждение&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "Командой могут пользоваться только игроки." 10 | no-permission: "Недостаточно прав для использования этой команды." 11 | plugin-cancelled: "Плагин отменил это действие." 12 | 13 | pet: 14 | doesnt-exist: "Питомец с данным именем не существует." 15 | numeric: "Размер питомца должен быть числом." 16 | 17 | player: 18 | not-found: "Данный игрок не найден." 19 | no-pet: "У вас нет питомца с данным именем." 20 | no-pet-other: "У этого игрока нет питомца с данным именем." 21 | already-own-pet: "У вас уже есть питомец с таким именем." 22 | 23 | changepetname: 24 | no-permission: "У вас недостаточно прав для смены имени питомца других игроков." 25 | success: "Успешно изменили имя питомца с %s &r&aна %s!" 26 | 27 | clearpet: 28 | success: "Питомец &b%s&a был успешно очищен!" 29 | 30 | healpet: 31 | success: "Питомец %s &r&a был успешно вылечен!" 32 | 33 | leveluppet: 34 | success: "Успешно повысили уровень питомца: &b%s &r&a %s!" 35 | 36 | spawnpet: 37 | no-permission: "У вас недостаточно прав для спавна этого питомца." 38 | no-permission-others: "У вас недостаточно прав для спавна питомцев другим игрокам." 39 | success: "Питомец &b%s&a был успешно заспавнен!" 40 | success-other: "Вам вручили питомца &b%s&a!" 41 | name: "Напишите новое имя для питомца в чат!" 42 | selecting-name: "%s в данный момент выбирает имя для своего питомца!" 43 | exceeded-limit: "%s достиг лимита питомцев." 44 | 45 | removepet: 46 | success: "Питомец &b%s&a был успешно удалён!" 47 | 48 | togglepet: 49 | no-pet-specified: "Вы не указали питомца для переключения." 50 | success: "Успешно переключили всех питомцев в режим %s!" 51 | success-others: "Успешно переключён питомец &b%s &r&aв режим %s!" 52 | ... 53 | -------------------------------------------------------------------------------- /resources/languages/vi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Vietnamese messages file for BlockPets, a pets plugin by BlockHorizons. 3 | 4 | prefix: 5 | warning: "&c[&6Cảnh báo&c]" 6 | 7 | commands: 8 | errors: 9 | console-use: "Khi sử dụng lệnh đó từ giao diện điều khiển, tất cả các đối số phải được cung cấp." 10 | no-console-use: "Chạy command trong server!" 11 | no-permission: "Bạn không có quyền sử dụng lệnh này!" 12 | plugin-cancelled: "Plugin đã hủy hành động này!" 13 | 14 | pet: 15 | doesnt-exist: "Không tồn tại tên Pet!" 16 | numeric: "Tên Pet không được phép để kí tự hay chữ số" 17 | 18 | player: 19 | not-found: "Không tìm thấy người chơi" 20 | no-pet: "Bạn không sở hữu pet với cái tên này!" 21 | no-pet-other: "Người chơi này không sở hữu pet với cái tên này!" 22 | already-own-pet: "Bạn đã sử dụng tên này!" 23 | 24 | changepetname: 25 | no-permission: "Bạn không có quyền đổi tên pet của người khác!" 26 | success: "Đổi tên Pet %s &r&asang %s thành công!" 27 | 28 | clearpet: 29 | success: "Hủy bỏ Pet &b%s&a thành công!" 30 | 31 | healpet: 32 | success: "Pet %s &r&a đã được hồi máu!" 33 | 34 | leveluppet: 35 | success: "Pet đã được thăng cấp: &b%s &r&a %s!" 36 | 37 | spawnpet: 38 | no-permission: "Bạn không có quyền spawn pet!" 39 | no-permission-others: "Bạn không có quyền spawn pet cho người khác" 40 | success: "Spawn pet thành công với tên: &b%s&a!" 41 | success-other: "Bạn đã nhận được Pet với tên: &b%s&a!" 42 | name: "Vui lòng đặt tên Pet cho bạn một lần chat (không ghi màu)" 43 | selecting-name: "%s đang chọn tên cho Pet của họ" 44 | exceeded-limit: "%s đã dùng quá số lượng Pet" 45 | 46 | removepet: 47 | success: "Hủy bỏ Pet thành công &b%s&a!" 48 | 49 | togglepet: 50 | no-pet-specified: "Bạn chưa xác định tên Pet để mở!" 51 | success: "Mở Pet thành công %s!" 52 | success-others: "Mở Pet cho người khác thành công &b%s &r&a %s!" 53 | ... 54 | -------------------------------------------------------------------------------- /resources/patches/mysql.sql: -------------------------------------------------------------------------------- 1 | -- #!mysql 2 | -- #{ version 3 | -- # { 1.1.2 4 | ALTER TABLE Pets ADD COLUMN Visible BOOL NOT NULL DEFAULT TRUE AFTER Chested; 5 | -- # } 6 | -- #} 7 | -------------------------------------------------------------------------------- /resources/patches/sqlite.sql: -------------------------------------------------------------------------------- 1 | -- #!sqlite 2 | -- #{ version 3 | -- # { 1.1.2 4 | ALTER TABLE Pets ADD COLUMN Visible BOOL NOT NULL DEFAULT TRUE; 5 | -- # } 6 | -- #} -------------------------------------------------------------------------------- /resources/pet_properties.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Walking Pets 3 | Axolotl: 4 | Can-Attack: true 5 | Can-Be-Ridden: true 6 | Can-Be-Chested: true 7 | Can-Sit-On-Owner: true 8 | Speed: 1.2 9 | Max-Size: 10 10 | CaveSpider: 11 | Can-Attack: true 12 | Can-Be-Ridden: true 13 | Can-Be-Chested: true 14 | Can-Sit-On-Owner: true 15 | Speed: 1.4 16 | Max-Size: 10 17 | Chicken: 18 | Can-Attack: true 19 | Can-Be-Ridden: true 20 | Can-Be-Chested: true 21 | Can-Sit-On-Owner: true 22 | Speed: 1.2 23 | Max-Size: 10 24 | Cow: 25 | Can-Attack: true 26 | Can-Be-Ridden: true 27 | Can-Be-Chested: true 28 | Can-Sit-On-Owner: true 29 | Speed: 1 30 | Max-Size: 10 31 | Creeper: 32 | Can-Attack: true 33 | Can-Be-Ridden: true 34 | Can-Be-Chested: true 35 | Can-Sit-On-Owner: true 36 | Speed: 1.2 37 | Max-Size: 10 38 | Donkey: 39 | Can-Attack: true 40 | Can-Be-Ridden: true 41 | Can-Be-Chested: true 42 | Can-Sit-On-Owner: true 43 | Speed: 1.4 44 | Max-Size: 10 45 | Enderman: 46 | Can-Attack: true 47 | Can-Be-Ridden: true 48 | Can-Be-Chested: true 49 | Can-Sit-On-Owner: true 50 | Speed: 1.4 51 | Max-Size: 10 52 | Endermite: 53 | Can-Attack: true 54 | Can-Be-Ridden: true 55 | Can-Be-Chested: true 56 | Can-Sit-On-Owner: true 57 | Speed: 1.4 58 | Max-Size: 10 59 | Evoker: 60 | Can-Attack: true 61 | Can-Be-Ridden: true 62 | Can-Be-Chested: true 63 | Can-Sit-On-Owner: true 64 | Speed: 1.4 65 | Max-Size: 10 66 | Fox: 67 | Can-Attack: true 68 | Can-Be-Ridden: true 69 | Can-Be-Chested: true 70 | Can-Sit-On-Owner: true 71 | Speed: 1.8 72 | Max-Size: 10 73 | Goat: 74 | Can-Attack: true 75 | Can-Be-Ridden: true 76 | Can-Be-Chested: true 77 | Can-Sit-On-Owner: true 78 | Speed: 1.4 79 | Max-Size: 10 80 | Horse: 81 | Can-Attack: true 82 | Can-Be-Ridden: true 83 | Can-Be-Chested: true 84 | Can-Sit-On-Owner: true 85 | Speed: 1.8 86 | Max-Size: 10 87 | Husk: 88 | Can-Attack: true 89 | Can-Be-Ridden: true 90 | Can-Be-Chested: true 91 | Can-Sit-On-Owner: true 92 | Speed: 1.2 93 | Max-Size: 10 94 | IronGolem: 95 | Can-Attack: true 96 | Can-Be-Ridden: true 97 | Can-Be-Chested: true 98 | Can-Sit-On-Owner: true 99 | Speed: 1.4 100 | Max-Size: 10 101 | Llama: 102 | Can-Attack: true 103 | Can-Be-Ridden: true 104 | Can-Be-Chested: true 105 | Can-Sit-On-Owner: true 106 | Speed: 1.4 107 | Max-Size: 10 108 | Mooshroom: 109 | Can-Attack: true 110 | Can-Be-Ridden: true 111 | Can-Be-Chested: true 112 | Can-Sit-On-Owner: true 113 | Speed: 1.2 114 | Max-Size: 10 115 | Mule: 116 | Can-Attack: true 117 | Can-Be-Ridden: true 118 | Can-Be-Chested: true 119 | Can-Sit-On-Owner: true 120 | Speed: 1.6 121 | Max-Size: 10 122 | Ocelot: 123 | Can-Attack: true 124 | Can-Be-Ridden: true 125 | Can-Be-Chested: true 126 | Can-Sit-On-Owner: true 127 | Speed: 1.8 128 | Max-Size: 10 129 | Pig: 130 | Can-Attack: true 131 | Can-Be-Ridden: true 132 | Can-Be-Chested: true 133 | Can-Sit-On-Owner: true 134 | Speed: 1 135 | Max-Size: 10 136 | PolarBear: 137 | Can-Attack: true 138 | Can-Be-Ridden: true 139 | Can-Be-Chested: true 140 | Can-Sit-On-Owner: true 141 | Speed: 1.2 142 | Max-Size: 10 143 | Sheep: 144 | Can-Attack: true 145 | Can-Be-Ridden: true 146 | Can-Be-Chested: true 147 | Can-Sit-On-Owner: true 148 | Speed: 1 149 | Max-Size: 10 150 | SilverFish: 151 | Can-Attack: true 152 | Can-Be-Ridden: true 153 | Can-Be-Chested: true 154 | Can-Sit-On-Owner: true 155 | Speed: 1.2 156 | Max-Size: 10 157 | SkeletonHorse: 158 | Can-Attack: true 159 | Can-Be-Ridden: true 160 | Can-Be-Chested: true 161 | Can-Sit-On-Owner: true 162 | Speed: 1.4 163 | Max-Size: 10 164 | Skeleton: 165 | Can-Attack: true 166 | Can-Be-Ridden: true 167 | Can-Be-Chested: true 168 | Can-Sit-On-Owner: true 169 | Speed: 1 170 | Max-Size: 10 171 | SnowGolem: 172 | Can-Attack: true 173 | Can-Be-Ridden: true 174 | Can-Be-Chested: true 175 | Can-Sit-On-Owner: true 176 | Speed: 1.2 177 | Max-Size: 10 178 | Spider: 179 | Can-Attack: true 180 | Can-Be-Ridden: true 181 | Can-Be-Chested: true 182 | Can-Sit-On-Owner: true 183 | Speed: 1.4 184 | Max-Size: 10 185 | Stray: 186 | Can-Attack: true 187 | Can-Be-Ridden: true 188 | Can-Be-Chested: true 189 | Can-Sit-On-Owner: true 190 | Speed: 1.2 191 | Max-Size: 10 192 | Strider: 193 | Can-Attack: true 194 | Can-Be-Ridden: true 195 | Can-Be-Chested: false 196 | Can-Sit-On-Owner: true 197 | Speed: 1.2 198 | Max-Size: 4 199 | Villager: 200 | Can-Attack: true 201 | Can-Be-Ridden: true 202 | Can-Be-Chested: true 203 | Can-Sit-On-Owner: true 204 | Speed: 1.2 205 | Max-Size: 10 206 | Vindicator: 207 | Can-Attack: true 208 | Can-Be-Ridden: true 209 | Can-Be-Chested: true 210 | Can-Sit-On-Owner: true 211 | Speed: 1.2 212 | Max-Size: 10 213 | Witch: 214 | Can-Attack: true 215 | Can-Be-Ridden: true 216 | Can-Be-Chested: true 217 | Can-Sit-On-Owner: true 218 | Speed: 1.2 219 | Max-Size: 10 220 | WitherSkeleton: 221 | Can-Attack: true 222 | Can-Be-Ridden: true 223 | Can-Be-Chested: true 224 | Can-Sit-On-Owner: true 225 | Speed: 1.4 226 | Max-Size: 10 227 | Wolf: 228 | Can-Attack: true 229 | Can-Be-Ridden: true 230 | Can-Be-Chested: true 231 | Can-Sit-On-Owner: true 232 | Speed: 1.6 233 | Max-Size: 10 234 | ZombieHorse: 235 | Can-Attack: true 236 | Can-Be-Ridden: true 237 | Can-Be-Chested: true 238 | Can-Sit-On-Owner: true 239 | Speed: 1.4 240 | Max-Size: 10 241 | Zombie: 242 | Can-Attack: true 243 | Can-Be-Ridden: true 244 | Can-Be-Chested: true 245 | Can-Sit-On-Owner: true 246 | Speed: 1 247 | Max-Size: 10 248 | ZombiePigman: 249 | Can-Attack: true 250 | Can-Be-Ridden: true 251 | Can-Be-Chested: true 252 | Can-Sit-On-Owner: true 253 | Speed: 1.2 254 | Max-Size: 10 255 | ZombieVillager: 256 | Can-Attack: true 257 | Can-Be-Ridden: true 258 | Can-Be-Chested: true 259 | Can-Sit-On-Owner: true 260 | Speed: 1.2 261 | Max-Size: 10 262 | Warden: 263 | Can-Attack: true 264 | Can-Be-Ridden: true 265 | Can-Be-Chested: true 266 | Can-Sit-On-Owner: true 267 | Speed: 1 268 | Max-Size: 10 269 | 270 | # Flying Pets 271 | Allay: 272 | Can-Attack: true 273 | Can-Be-Ridden: true 274 | Can-Be-Chested: true 275 | Can-Sit-On-Owner: true 276 | Speed: 1 277 | Flying-Height: 5 278 | Max-Size: 10 279 | Arrow: 280 | Can-Attack: true 281 | Can-Be-Ridden: true 282 | Can-Be-Chested: true 283 | Can-Sit-On-Owner: true 284 | Speed: 3 285 | Flying-Height: 25 286 | Max-Size: 10 287 | Bat: 288 | Can-Attack: true 289 | Can-Be-Ridden: true 290 | Can-Be-Chested: true 291 | Can-Sit-On-Owner: true 292 | Speed: 1 293 | Flying-Height: 8 294 | Max-Size: 10 295 | Bee: 296 | Can-Attack: true 297 | Can-Be-Ridden: true 298 | Can-Be-Chested: true 299 | Can-Sit-On-Owner: true 300 | Speed: 1 301 | Flying-Height: 8 302 | Max-Size: 10 303 | Blaze: 304 | Can-Attack: true 305 | Can-Be-Ridden: true 306 | Can-Be-Chested: true 307 | Can-Sit-On-Owner: true 308 | Speed: 1.2 309 | Flying-Height: 13 310 | Max-Size: 10 311 | EnderCrystal: 312 | Can-Attack: true 313 | Can-Be-Ridden: true 314 | Can-Be-Chested: true 315 | Can-Sit-On-Owner: true 316 | Speed: 1.2 317 | Flying-Height: 13 318 | Max-Size: 10 319 | EnderDragon: 320 | Can-Attack: true 321 | Can-Be-Ridden: true 322 | Can-Be-Chested: true 323 | Can-Sit-On-Owner: true 324 | Speed: 1.8 325 | Flying-Height: 60 326 | Max-Size: 5 327 | Ghast: 328 | Can-Attack: true 329 | Can-Be-Ridden: true 330 | Can-Be-Chested: true 331 | Can-Sit-On-Owner: true 332 | Speed: 1.3 333 | Flying-Height: 25 334 | Max-Size: 10 335 | Vex: 336 | Can-Attack: true 337 | Can-Be-Ridden: true 338 | Can-Be-Chested: true 339 | Can-Sit-On-Owner: true 340 | Speed: 1.4 341 | Flying-Height: 25 342 | Max-Size: 10 343 | Wither: 344 | Can-Attack: true 345 | Can-Be-Ridden: true 346 | Can-Be-Chested: true 347 | Can-Sit-On-Owner: true 348 | Speed: 1.8 349 | Flying-Height: 60 350 | Max-Size: 10 351 | WitherSkull: 352 | Can-Attack: true 353 | Can-Be-Ridden: true 354 | Can-Be-Chested: true 355 | Can-Sit-On-Owner: true 356 | Speed: 1.2 357 | Flying-Height: 13 358 | Max-Size: 10 359 | 360 | # Swimming Pets 361 | ElderGuardian: 362 | Can-Attack: true 363 | Can-Be-Ridden: true 364 | Can-Be-Chested: true 365 | Can-Sit-On-Owner: true 366 | Speed: 1 367 | Swimming-Speed: 2 368 | Jumping-Height: 0.12 369 | Max-Size: 10 370 | Guardian: 371 | Can-Attack: true 372 | Can-Be-Ridden: true 373 | Can-Be-Chested: true 374 | Can-Sit-On-Owner: true 375 | Speed: 1 376 | Swimming-Speed: 1.8 377 | Jumping-Height: 0.1 378 | Max-Size: 10 379 | Squid: 380 | Can-Attack: true 381 | Can-Be-Ridden: true 382 | Can-Be-Chested: true 383 | Can-Sit-On-Owner: true 384 | Speed: 1 385 | Swimming-Speed: 1.4 386 | Jumping-Height: 0.1 387 | Max-Size: 10 388 | 389 | # Bouncing Pets 390 | MagmaCube: 391 | Can-Attack: true 392 | Can-Be-Ridden: true 393 | Can-Be-Chested: true 394 | Can-Sit-On-Owner: true 395 | Speed: 1.4 396 | Jumping-Height: 0.14 397 | Max-Size: 10 398 | Rabbit: 399 | Can-Attack: true 400 | Can-Be-Ridden: true 401 | Can-Be-Chested: true 402 | Can-Sit-On-Owner: true 403 | Speed: 1.2 404 | Jumping-Height: 0.1 405 | Max-Size: 10 406 | Slime: 407 | Can-Attack: true 408 | Can-Be-Ridden: true 409 | Can-Be-Chested: true 410 | Can-Sit-On-Owner: true 411 | Speed: 1.6 412 | Jumping-Height: 0.14 413 | Max-Size: 10 414 | ... -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/Loader.php: -------------------------------------------------------------------------------- 1 | AllayPet::class, 104 | "Arrow" => ArrowPet::class, 105 | "Axolotl" => AxolotlPet::class, 106 | "Bat" => BatPet::class, 107 | "Bee" => BeePet::class, 108 | "Blaze" => BlazePet::class, 109 | "CaveSpider" => CaveSpiderPet::class, 110 | "Chicken" => ChickenPet::class, 111 | "Cow" => CowPet::class, 112 | "Creeper" => CreeperPet::class, 113 | "Donkey" => DonkeyPet::class, 114 | "ElderGuardian" => ElderGuardianPet::class, 115 | "EnderCrystal" => EnderCrystalPet::class, 116 | "EnderDragon" => EnderDragonPet::class, 117 | "Enderman" => EndermanPet::class, 118 | "Endermite" => EndermitePet::class, 119 | "Evoker" => EvokerPet::class, 120 | "Fox" => FoxPet::class, 121 | "Ghast" => GhastPet::class, 122 | "Goat" => GoatPet::class, 123 | "Guardian" => GuardianPet::class, 124 | "Horse" => HorsePet::class, 125 | "Husk" => HuskPet::class, 126 | "IronGolem" => IronGolemPet::class, 127 | "Llama" => LlamaPet::class, 128 | "MagmaCube" => MagmaCubePet::class, 129 | "Mooshroom" => MooshroomPet::class, 130 | "Mule" => MulePet::class, 131 | "Ocelot" => OcelotPet::class, 132 | "Pig" => PigPet::class, 133 | "PolarBear" => PolarBearPet::class, 134 | "Rabbit" => RabbitPet::class, 135 | "Sheep" => SheepPet::class, 136 | "SilverFish" => SilverFishPet::class, 137 | "Skeleton" => SkeletonPet::class, 138 | "SkeletonHorse" => SkeletonHorsePet::class, 139 | "Slime" => SlimePet::class, 140 | "SnowGolem" => SnowGolemPet::class, 141 | "Spider" => SpiderPet::class, 142 | "Squid" => SquidPet::class, 143 | "Stray" => StrayPet::class, 144 | "Strider" => StriderPet::class, 145 | "Vex" => VexPet::class, 146 | "Villager" => VillagerPet::class, 147 | "Vindicator" => VindicatorPet::class, 148 | "Warden" => WardenPet::class, 149 | "Witch" => WitchPet::class, 150 | "Wither" => WitherPet::class, 151 | "WitherSkeleton" => WitherSkeletonPet::class, 152 | "WitherSkull" => WitherSkullPet::class, 153 | "Wolf" => WolfPet::class, 154 | "Zombie" => ZombiePet::class, 155 | "ZombieHorse" => ZombieHorsePet::class, 156 | "ZombiePigman" => ZombiePigmanPet::class, 157 | "ZombieVillager" => ZombieVillagerPet::class 158 | ]; 159 | 160 | /** @var string[] */ 161 | private array $availableLanguages = [ 162 | "en", 163 | "nl", 164 | "vi", 165 | "gr", 166 | "ko", 167 | "de" 168 | ]; 169 | 170 | public array $selectingName = []; 171 | 172 | private BlockPetsConfig $bpConfig; 173 | private PetProperties $pProperties; 174 | private LanguageConfig $language; 175 | private ?BaseDataStorer $database = null; 176 | 177 | /** @var BasePet[][] */ 178 | private array $playerPets = []; 179 | 180 | protected function onEnable(): void { 181 | if(!is_dir($this->getDataFolder())) { 182 | mkdir($this->getDataFolder()); 183 | } 184 | 185 | $this->saveResource(".version_file"); 186 | 187 | $database_stmts = Path::join($this->getDataFolder(), "database_stmts"); 188 | if(!is_dir($database_stmts)) { 189 | mkdir($database_stmts); 190 | } 191 | 192 | $this->saveResource("database_stmts/mysql.sql", true); 193 | $this->saveResource("database_stmts/sqlite.sql", true); 194 | 195 | $this->registerEntities(); 196 | $this->registerItems(); 197 | $this->registerCommands(); 198 | $this->registerListeners(); 199 | PetInventoryManager::init($this); 200 | 201 | $this->bpConfig = new BlockPetsConfig($this); 202 | $this->pProperties = new PetProperties($this); 203 | $this->language = new LanguageConfig($this); 204 | $this->selectDatabase(); 205 | 206 | /** @var AttributeFactory $factory */ 207 | $factory = AttributeFactory::getInstance(); 208 | $factory->register(Attribute::HORSE_JUMP_STRENGTH, 0.0, 3.0, 0.6679779); 209 | 210 | $this->checkVersionChange(); 211 | } 212 | 213 | private function checkVersionChange(): void { 214 | $version_file = $this->getDataFolder() . ".version_file"; 215 | if(!is_file($version_file)) { 216 | $current_version = "1.1.0"; 217 | } else { 218 | $current_version = yaml_parse_file($version_file)["version"]; 219 | } 220 | 221 | if(version_compare($this->getDescription()->getVersion(), $current_version, '>')) { 222 | $this->updateVersion($current_version); 223 | } 224 | } 225 | 226 | private function updateVersion(string $current_version): void { 227 | $current = (int) str_replace(".", "", $current_version); 228 | $newest = (int) str_replace(".", "", $this->getDescription()->getVersion()); 229 | while($current < $newest) { 230 | ++$current; 231 | $version = implode(".", str_split((string) $current)); 232 | $this->onVersionUpdate($version); 233 | } 234 | 235 | $this->saveResource(".version_file", true); 236 | } 237 | 238 | private function onVersionUpdate(string $version): void { 239 | $this->getDatabase()->patch($version); 240 | } 241 | 242 | public function registerCommands(): void { 243 | $this->getServer()->getCommandMap()->registerAll($this->getName(), [ 244 | new AddPetPointsCommand($this), 245 | new SpawnPetCommand($this), 246 | new LevelUpPetCommand($this), 247 | new RemovePetCommand($this), 248 | new HealPetCommand($this), 249 | new ClearPetCommand($this), 250 | new TogglePetCommand($this), 251 | new ChangePetNameCommand($this), 252 | new ListPetsCommand($this), 253 | new PetsTopCommand($this), 254 | new PetCommand($this) 255 | ]); 256 | } 257 | 258 | public function registerEntities(): void { 259 | /** @var EntityFactory $entityFactory */ 260 | $entityFactory = EntityFactory::getInstance(); 261 | /** 262 | * @var string $name 263 | * @var BasePet $petClass 264 | */ 265 | foreach(self::PETS as $name => $petClass) { 266 | $entityFactory->register($petClass, function(World $world, CompoundTag $nbt) use ($petClass): Entity { 267 | return new $petClass(EntityDataHelper::parseLocation($nbt, $world), $nbt); 268 | }, [$name, $petClass::NETWORK_NAME]); 269 | } 270 | } 271 | 272 | public function registerItems(): void { 273 | /** @var ItemFactory $factory */ 274 | $factory = ItemFactory::getInstance(); 275 | $factory->register(new Saddle(), true); 276 | // Item::addCreativeItem(Item::get(Item::SADDLE)); 277 | } 278 | 279 | public function registerListeners(): void { 280 | $listeners = [ 281 | new EventListener($this), 282 | new RidingListener($this) 283 | ]; 284 | foreach($listeners as $listener) { 285 | $this->getServer()->getPluginManager()->registerEvents($listener, $this); 286 | } 287 | } 288 | 289 | /** 290 | * @return string[] 291 | */ 292 | public function getAvailableLanguages(): array { 293 | return $this->availableLanguages; 294 | } 295 | 296 | private function selectDatabase(): bool { 297 | switch(strtolower($this->getBlockPetsConfig()->getDatabase())) { 298 | default: 299 | case "mysql": 300 | $this->database = new MySQLDataStorer($this); 301 | break; 302 | case "sqlite": 303 | case "sqlite3": 304 | $this->database = new SQLiteDataStorer($this); 305 | break; 306 | } 307 | return true; 308 | } 309 | 310 | public function getBlockPetsConfig(): BlockPetsConfig { 311 | return $this->bpConfig; 312 | } 313 | 314 | public function translate(string $key, array $params = []): string { 315 | if(!empty($params)) { 316 | return vsprintf($this->getLanguage()->get($key), $params); 317 | } 318 | return $this->getLanguage()->get($key); 319 | } 320 | 321 | public function getLanguage(): LanguageConfig { 322 | return $this->language; 323 | } 324 | 325 | /** 326 | * Checks if a pet type of that name exists. 327 | */ 328 | public function petExists(string $entityName): bool { 329 | return $this->getPet($entityName) !== null; 330 | } 331 | 332 | /** 333 | * Tries to match a pet type with the pet type list, and returns the fully qualified name if this could be found. Null if no valid result was found. 334 | */ 335 | public function getPet(string $entityName): ?string { 336 | foreach(self::PETS as $pet => $petClass) { 337 | if(strtolower($pet) === strtolower($entityName)) { 338 | return $pet; 339 | } 340 | } 341 | return null; 342 | } 343 | 344 | /** 345 | * Get the class of the relevant pet. 346 | */ 347 | public function getPetClass(string $entityName): ?string { 348 | foreach(self::PETS as $pet => $petClass) { 349 | if(strtolower($pet) === strtolower($entityName)) { 350 | return $petClass; 351 | } 352 | } 353 | return null; 354 | } 355 | 356 | /** 357 | * Creates a new pet for the given player. 358 | */ 359 | public function createPet(string $entityName, Player $player, string $name, float $scale = 1.0, bool $isBaby = false, int $level = 1, int $levelPoints = 0, bool $chested = false, bool $isVisible = true, ?string $inventory = null): ?BasePet { 360 | $pet = $this->getPetByName($name, $player->getName()); 361 | if($pet !== null) { 362 | $this->removePet($pet); 363 | } 364 | 365 | $nbt = CompoundTag::create(); 366 | $nbt->setString("petOwner", $player->getName()); 367 | $nbt->setFloat("scale", $scale); 368 | $nbt->setString("petName", $name); 369 | $nbt->setInt("petLevel", $level); 370 | $nbt->setInt("petLevelPoints", $levelPoints); 371 | $nbt->setByte("isBaby", (int) $isBaby); 372 | $nbt->setByte("chested", (int) $chested); 373 | 374 | $class = $this->getPetClass($entityName); 375 | if($class === null) { 376 | return null; 377 | } 378 | 379 | $entity = new $class($player->getLocation(), $nbt); 380 | if($entity instanceof BasePet) { 381 | if(!empty($inventory)) { 382 | $entity->getInventoryManager()->load($inventory); 383 | } 384 | 385 | $ev = new PetSpawnEvent($this, $entity); 386 | $ev->call(); 387 | 388 | if($ev->isCancelled()) { 389 | $this->removePet($entity); 390 | return null; 391 | } 392 | 393 | if(!$isVisible) { 394 | $entity->updateVisibility(false); 395 | } else { 396 | $entity->spawnToAll(); 397 | } 398 | 399 | $this->playerPets[strtolower($player->getName())][strtolower($entity->getPetName())] = $entity; 400 | return $entity; 401 | } 402 | 403 | return null; 404 | } 405 | 406 | /** 407 | * Creates a copy of the given pet and returns it. 408 | */ 409 | public function clonePet(PetData $data): ?BasePet { 410 | $owner = $this->getServer()->getPlayerExact($data->getOwnerName()); 411 | if($owner === null) { 412 | return null; 413 | } 414 | return $this->createPet( 415 | $data->getPetId(), 416 | $owner, 417 | $data->getPetName(), 418 | $data->getScale(), 419 | $data->isBaby(), 420 | $data->getLevel(), 421 | $data->getLevelPoints(), 422 | $data->isChested(), 423 | $data->isVisible(), 424 | $data->getInventory() 425 | ); 426 | } 427 | 428 | /** 429 | * Gets all currently available pets from the given player. 430 | * 431 | * @return BasePet[] 432 | */ 433 | public function getPetsFrom(Player $player): array { 434 | return $this->playerPets[strtolower($player->getName())] ?? []; 435 | } 436 | 437 | /** 438 | * Returns the first pet found with the given name. 439 | */ 440 | public function getPetByName(string $name, ?string $player = null): ?BasePet { 441 | $name = strtolower($name); 442 | if($player !== null) { 443 | return $this->playerPets[strtolower($player)][$name] ?? null; 444 | } 445 | foreach($this->getServer()->getOnlinePlayers() as $player) { 446 | if(isset($this->playerPets[$k = strtolower($player->getName())][$name])) { 447 | return $this->playerPets[$k][$name]; 448 | } 449 | } 450 | return null; 451 | } 452 | 453 | /** 454 | * Closes and removes the specified pet from cache and calls PetRemoveEvent events. 455 | */ 456 | public function removePet(BasePet $pet, bool $close = true): void { 457 | // TODO: Call a cancellable event if this method isn't called when pet owner quits 458 | (new PetRemoveEvent($this, $pet))->call(); 459 | if($pet->isRidden()) { 460 | $pet->throwRiderOff(); 461 | } 462 | if($close && !$pet->isClosed()) { 463 | $pet->close(); 464 | } 465 | unset($this->playerPets[strtolower($pet->getPetOwnerName())][strtolower($pet->getPetName())]); 466 | } 467 | 468 | /** 469 | * Returns the database to store and fetch data from. 470 | */ 471 | public function getDatabase(): BaseDataStorer { 472 | if($this->database === null) { 473 | throw new \RuntimeException("Attempted to retrieve the database while database storing was unavailable."); 474 | } 475 | return $this->database; 476 | } 477 | 478 | /** 479 | * Gets the pet the given player is currently riding. 480 | */ 481 | public function getRiddenPet(Player $player): ?BasePet { 482 | foreach($this->getPetsFrom($player) as $pet) { 483 | if($pet->isRidden()) { 484 | return $pet; 485 | } 486 | } 487 | return null; 488 | } 489 | 490 | /** 491 | * Checks if the given player is currently riding a pet. 492 | */ 493 | public function isRidingAPet(Player $player): bool { 494 | foreach($this->getPetsFrom($player) as $pet) { 495 | if($pet->isRidden()) { 496 | return true; 497 | } 498 | } 499 | return false; 500 | } 501 | 502 | public function getPetProperties(): PetProperties { 503 | return $this->pProperties; 504 | } 505 | } 506 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/AddPetPointsCommand.php: -------------------------------------------------------------------------------- 1 | [amount] [player]", ["app"]); 14 | $this->setPermission("blockpets.command.addpetpoints"); 15 | } 16 | 17 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 18 | if(!isset($args[0])) { 19 | return false; 20 | } 21 | 22 | $loader = $this->getLoader(); 23 | if(($pet = $loader->getPetByName($args[0])) === null) { 24 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.doesnt-exist")); 25 | return true; 26 | } 27 | 28 | $amount = 1; 29 | if(isset($args[1])) { 30 | if(is_numeric($args[1])) { 31 | $amount = (int) $args[1]; 32 | } 33 | } 34 | 35 | if(isset($args[2])) { 36 | if(($player = $loader->getServer()->getPlayerByPrefix($args[2])) === null) { 37 | $this->sendWarning($sender, $loader->translate("commands.errors.player.not-found")); 38 | return true; 39 | } 40 | if(($pet = $loader->getPetByName($args[0], $player->getName())) === null) { 41 | $this->sendWarning($sender, $loader->translate("commands.errors.player.no-pet-other")); 42 | return true; 43 | } 44 | $pet->addPetLevelPoints($amount); 45 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.addpetpoints.success", [$amount, $pet->getPetName()])); 46 | return true; 47 | } 48 | 49 | $pet->addPetLevelPoints($amount); 50 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.addpetpoints.success", [$amount, $pet->getPetName()])); 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/BaseCommand.php: -------------------------------------------------------------------------------- 1 | sendWarning($sender, $this->getLoader()->translate("commands.errors.no-console-use")); 24 | return; 25 | } 26 | $this->sendWarning($sender, $this->getLoader()->translate("commands.errors.console-use")); 27 | } 28 | 29 | public function sendWarning(CommandSender $sender, string $text): void { 30 | $sender->sendMessage(TF::RED . $this->getLoader()->translate("prefix.warning") . " " . $text); 31 | } 32 | 33 | public function sendPermissionMessage(CommandSender $sender): void { 34 | $this->sendWarning($sender, ($this->getLoader()->translate("commands." . $this->getName() . ".no-permission") !== "" ? $this->getLoader()->translate("commands." . $this->getName() . ".no-permission") : $this->getLoader()->translate("commands.no-permission"))); 35 | } 36 | 37 | public function getLoader(): Loader { 38 | return $this->loader; 39 | } 40 | 41 | public function getOwningPlugin(): Loader { 42 | return $this->loader; 43 | } 44 | 45 | /** 46 | * @param string[] $args 47 | */ 48 | public final function execute(CommandSender $sender, string $commandLabel, array $args): void { 49 | if(!$this->testPermissionSilent($sender)) { 50 | $this->sendPermissionMessage($sender); 51 | return; 52 | } 53 | 54 | if(!$this->onCommand($sender, $commandLabel, $args) && $this->usageMessage !== "") { 55 | $sender->sendMessage(str_replace("/" . $this->getName(), "/" . $commandLabel, $this->getUsage())); 56 | return; 57 | } 58 | } 59 | 60 | public abstract function onCommand(CommandSender $sender, string $commandLabel, array $args): bool; 61 | } 62 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/ChangePetNameCommand.php: -------------------------------------------------------------------------------- 1 | [player]", ["cpn, chpn"]); 15 | $this->setPermission("blockpets.command.changepetname.use"); 16 | } 17 | 18 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 19 | if(!($sender instanceof Player) && count($args) !== 3) { 20 | $this->sendConsoleError($sender); 21 | return false; 22 | } 23 | 24 | if(!isset($args[1]) || empty(trim($args[1]))) { 25 | $this->sendWarning($sender, "The name you entered is invalid."); 26 | return true; 27 | } 28 | 29 | $newName = $args[1]; 30 | 31 | if(isset($args[2])) { 32 | if($sender instanceof Player && $sender->hasPermission("blockpets.command.changepetname.others")) { 33 | $this->sendPermissionMessage($sender); 34 | return true; 35 | } 36 | if(($player = $this->getLoader()->getServer()->getPlayerByPrefix($args[2])) === null) { 37 | $this->sendWarning($sender, $this->getLoader()->translate("commands.errors.player.not-found")); 38 | return true; 39 | } 40 | if(($pet = $this->getLoader()->getPetByName($args[0], $player->getName())) === null) { 41 | $this->sendWarning($sender, $this->getLoader()->translate("commands.errors.player.no-pet-other")); 42 | return true; 43 | } 44 | $oldName = $pet->getPetName(); 45 | $pet->changeName($newName); 46 | $sender->sendMessage(TF::GREEN . $this->getLoader()->translate("commands.changepetname.success", [ 47 | $oldName, 48 | $newName 49 | ])); 50 | return true; 51 | } 52 | 53 | if(($pet = $this->getLoader()->getPetByName($args[0], $sender->getName())) === null) { 54 | $this->sendWarning($sender, $this->getLoader()->translate("commands.errors.player.no-pet")); 55 | return true; 56 | } 57 | 58 | $oldName = $pet->getPetName(); 59 | $pet->changeName($newName); 60 | $sender->sendMessage(TF::GREEN . $this->getLoader()->translate("commands.changepetname.success", [ 61 | $oldName, 62 | $newName 63 | ])); 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/ClearPetCommand.php: -------------------------------------------------------------------------------- 1 | ", ["cp"]); 15 | $this->setPermission("blockpets.command.clearpet"); 16 | } 17 | 18 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 19 | if(!($sender instanceof Player)) { 20 | $this->sendConsoleError($sender, true); 21 | return true; 22 | } 23 | 24 | if(!isset($args[0])) { 25 | return false; 26 | } 27 | 28 | $loader = $this->getLoader(); 29 | if(($pet = $loader->getPetByName($args[0], $sender->getName())) === null) { 30 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.doesnt-exist")); 31 | return true; 32 | } 33 | 34 | $loader->removePet($pet); 35 | $loader->getDatabase()->unregisterPet($pet); 36 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.removepet.success", [$pet->getPetName()])); 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/HealPetCommand.php: -------------------------------------------------------------------------------- 1 | [player]", ["hp", "petheal"]); 15 | $this->setPermission("blockpets.command.healpet"); 16 | } 17 | 18 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 19 | if(!isset($args[0])) { 20 | return false; 21 | } 22 | 23 | $loader = $this->getLoader(); 24 | if(($pet = $loader->getPetByName($args[0])) === null) { 25 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.doesnt-exist")); 26 | return true; 27 | } 28 | 29 | if(isset($args[1])) { 30 | if(($player = $loader->getServer()->getPlayerByPrefix($args[1])) === null) { 31 | $this->sendWarning($sender, $loader->translate("commands.errors.player.not-found")); 32 | return true; 33 | } 34 | if(($pet = $loader->getPetByName($args[0], $player->getName())) === null) { 35 | $this->sendWarning($sender, $loader->translate("commands.errors.player.no-pet-other")); 36 | return true; 37 | } 38 | } 39 | 40 | $pet->fullHeal(); 41 | $pet->getWorld()->addParticle($pet->getLocation()->add(0, 2, 0), new HeartParticle(4)); 42 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.healpet.success", [$pet->getPetName()])); 43 | return true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/LevelUpPetCommand.php: -------------------------------------------------------------------------------- 1 | [amount] [player]", ["lup"]); 14 | $this->setPermission("blockpets.command.leveluppet"); 15 | } 16 | 17 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 18 | if(!isset($args[0])) { 19 | return false; 20 | } 21 | 22 | $loader = $this->getLoader(); 23 | if(($pet = $loader->getPetByName($args[0])) === null) { 24 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.doesnt-exist")); 25 | return true; 26 | } 27 | 28 | $amount = 1; 29 | if(isset($args[1])) { 30 | if(is_numeric($args[1])) { 31 | $amount = (int) $args[1]; 32 | } 33 | } 34 | 35 | if(isset($args[2])) { 36 | if(($player = $loader->getServer()->getPlayerByPrefix($args[2])) === null) { 37 | $this->sendWarning($sender, $loader->translate("commands.errors.player.not-found")); 38 | return true; 39 | } 40 | if(($pet = $loader->getPetByName($args[0], $player->getName())) === null) { 41 | $this->sendWarning($sender, $loader->translate("commands.errors.player.no-pet-other")); 42 | return true; 43 | } 44 | $pet->levelUp($amount); 45 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.leveluppet.success", [$pet->getPetName(), ($amount === 1 ? " once" : " " . $amount . " times")])); 46 | return true; 47 | } 48 | 49 | $pet->levelUp($amount); 50 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.leveluppet.success", [$pet->getPetName(), ($amount === 1 ? " once" : " " . $amount . " times")])); 51 | return true; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/ListPetsCommand.php: -------------------------------------------------------------------------------- 1 | setPermission("blockpets.command.listpets"); 18 | } 19 | 20 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 21 | if(!($sender instanceof Player)) { 22 | $this->sendConsoleError($sender, true); 23 | return true; 24 | } 25 | 26 | if(isset($args[0])) { 27 | if(isset($args[1])) { 28 | [$list_type, $page] = $args; 29 | } else { 30 | if(is_numeric($args[0])) { 31 | $page = $args[0]; 32 | } else { 33 | $list_type = $args[0]; 34 | } 35 | } 36 | } 37 | 38 | $loader = $this->getLoader(); 39 | 40 | if(isset($list_type)) { 41 | $list_type = $loader->getPet($list_type); 42 | if($list_type === null) { 43 | $sender->sendMessage($loader->translate("commands.errors.pet.doesnt-exist")); 44 | return true; 45 | } 46 | } else { 47 | $list_type = null; 48 | } 49 | 50 | if(isset($page)) { 51 | $page = max(1, (int) $page); 52 | } else { 53 | $page = 1; 54 | } 55 | 56 | $loader->getDatabase()->getPlayerPets( 57 | $sender->getName(), 58 | $list_type, 59 | function(array $rows) use($loader, $list_type, $page, $sender): void { 60 | $pets = []; 61 | $pets_c = 0; 62 | 63 | foreach($rows as ["PetName" => $petName, "EntityName" => $entityName, "Visible" => $isVisible]) { 64 | $row = TextFormat::YELLOW . ++$pets_c . ". " . $petName . ($list_type === null ? TextFormat::GRAY . " (" . $entityName . ") " : ""); 65 | if(!$isVisible) { 66 | $row .= TextFormat::GRAY . "[INVISIBLE]"; 67 | } 68 | $pets[] = $row; 69 | } 70 | 71 | if(empty($pets)) { 72 | if($list_type === null) { 73 | $sender->sendMessage($loader->translate("commands.listpets.no-pets-found")); 74 | } else { 75 | $sender->sendMessage($loader->translate("commands.listpets.no-pets-found-type", [$list_type])); 76 | } 77 | } else { 78 | $pages = (int) ceil($pets_c / self::ENTRIES_PER_PAGE); 79 | if($page > $pages) { 80 | $page = 1; 81 | } 82 | 83 | $message = TextFormat::GREEN . "--- Your " . ($list_type ?? "Pet") . "s " . TextFormat::YELLOW . $page . "/" . $pages . TextFormat::GREEN . " ---" . TextFormat::EOL; 84 | $message .= implode(TextFormat::EOL, array_slice($pets, 0, self::ENTRIES_PER_PAGE)); 85 | $sender->sendMessage($message); 86 | } 87 | } 88 | ); 89 | return true; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/PetCommand.php: -------------------------------------------------------------------------------- 1 | [petName|select] [size] [isBaby] [player]: Spawns a new pet with the given data.", 17 | "/removepet [player]: Removes the first pet with the given name, and checks for a player if given.", 18 | "/togglepet [player]: Toggles a pet on/off, depending on the current state.", 19 | "/healpet [player]: Heals the first pet with the given name, and checks for a player if given.", 20 | "/leveluppet [amount] [player]: Levels up the first pet with the given name by the amount, and checks for player if given.", 21 | "/addpetpoints [amount] [player]: Adds pet points to the first pet with the given name by the amount, and checks for player if given.", 22 | "/clearpet : Clears one of your own pets with the given name.", 23 | "/changepetname [player]: Changes the name of one of your pets, or the pet of other players if specified.", 24 | "/listpets [EntityName=ALL] [page=1]: Lists all your pets.", 25 | "/petstop [EntityName=ALL] [page=1]: Displays pets leaderboard.", 26 | ]; 27 | 28 | const HELP_MESSAGES_PER_PAGE = 4;//When executed in console, all help messages are displayed irrespective of the page specified. 29 | 30 | /** @var string[][] */ 31 | private $help_messages = []; 32 | 33 | public function __construct(Loader $loader) { 34 | parent::__construct($loader, "pet", "Show info or reload BlockPets", "/pet [help|info|reload]", ["pets"]); 35 | $this->setPermission("blockpets.command.pet"); 36 | $this->formatHelpMessages(); 37 | } 38 | 39 | private function formatHelpMessages(): void { 40 | $messages = []; 41 | $sorted_messages = self::HELP_MESSAGES; 42 | sort($sorted_messages, SORT_STRING); 43 | 44 | foreach($sorted_messages as $message) { 45 | [$command_part, $description] = explode(":", $message, 2); 46 | [$command, $args_string] = explode(" ", $command_part, 2); 47 | 48 | $search_for = null; 49 | $starting_offset = NAN; 50 | $is_optional = null; 51 | $args = []; 52 | 53 | foreach(str_split($args_string) as $offset => $char) { 54 | if($search_for !== null) { 55 | if($char === $search_for) { 56 | $arg = substr($args_string, $starting_offset, $offset - $starting_offset + 1); 57 | if($is_optional) { 58 | $arg = TextFormat::GRAY . $arg; 59 | } else { 60 | $arg = TextFormat::RED . $arg; 61 | } 62 | $args[] = $arg; 63 | 64 | $starting_offset = -1; 65 | $search_for = null; 66 | $is_optional = null; 67 | } 68 | } else { 69 | switch($char) { 70 | case "<": 71 | $search_for = ">"; 72 | $starting_offset = $offset; 73 | $is_optional = false; 74 | break; 75 | case "[": 76 | $search_for = "]"; 77 | $starting_offset = $offset; 78 | $is_optional = true; 79 | break; 80 | } 81 | } 82 | } 83 | 84 | $messages[substr($command, 1)] = TextFormat::GREEN . $command . " " . implode(" ", $args) . TextFormat::YELLOW . ":" . $description; 85 | } 86 | 87 | $this->help_messages = array_chunk($messages, self::HELP_MESSAGES_PER_PAGE, true); 88 | } 89 | 90 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 91 | if(!isset($args[0])) { 92 | $args[0] = "help"; 93 | } 94 | 95 | switch(strtolower($args[0])) { 96 | case "help": 97 | if($sender instanceof ConsoleCommandSender) { 98 | $message = ""; 99 | foreach($this->help_messages as $messages) { 100 | foreach($messages as $msg) { 101 | $message .= $msg . TextFormat::EOL; 102 | } 103 | } 104 | 105 | $sender->sendMessage(strtr(self::HELP_MESSAGE_TITLE, [ 106 | "{CURRENT_PAGE}" => 1, 107 | "{TOTAL_PAGES}" => 1 108 | ]) . TextFormat::EOL . $message); 109 | return true; 110 | } 111 | 112 | if(!isset($args[1]) || !is_numeric($args[1])) { 113 | $args[1] = 1; 114 | } 115 | 116 | $page = (int) $args[1]; 117 | if(!isset($this->help_messages[$page - 1])) { 118 | $page = 1; 119 | } 120 | 121 | $sender->sendMessage(strtr(self::HELP_MESSAGE_TITLE, [ 122 | "{CURRENT_PAGE}" => $page, 123 | "{TOTAL_PAGES}" => count($this->help_messages) 124 | ]) . TextFormat::EOL . implode(TextFormat::EOL, $this->help_messages[$page - 1])); 125 | break; 126 | default: 127 | case "info": 128 | $loader = $this->getLoader(); 129 | $sender->sendMessage(TextFormat::AQUA . "[BlockPets] Information\n" . 130 | TextFormat::GREEN . "Version: " . TextFormat::YELLOW . $loader->getDescription()->getVersion() . "\n" . 131 | TextFormat::GREEN . "Target API: " . TextFormat::YELLOW . implode(", ", $loader->getDescription()->getCompatibleApis()) . "\n" . 132 | TextFormat::GREEN . "Organization: " . TextFormat::YELLOW . "BlockHorizons (https://github.com/BlockHorizons/BlockPets)\n" . 133 | TextFormat::GREEN . "Authors: " . TextFormat::YELLOW . "Sandertv (@Sandertv), Luke (@lukeeey)"); 134 | break; 135 | } 136 | return true; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/PetsTopCommand.php: -------------------------------------------------------------------------------- 1 | setPermission("blockpets.command.petstop"); 17 | } 18 | 19 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 20 | $loader = $this->getLoader(); 21 | 22 | if(isset($args[1]) || (isset($args[0]) && !is_numeric($args[0]))) { 23 | $entityName = $args[0]; 24 | $page = max(1, (int) ($args[1] ?? 1)); 25 | } else { 26 | $page = max(1, (int) ($args[0] ?? 1)); 27 | $entityName = null; 28 | } 29 | 30 | if($entityName !== null) { 31 | $entityName = $loader->getPet($entityName); 32 | if($entityName === null) { 33 | $sender->sendMessage($loader->translate("commands.errors.pet.doesnt-exist")); 34 | return true; 35 | } 36 | } 37 | 38 | $loader->getDatabase()->getPetsLeaderboard( 39 | ($page - 1) * self::ENTRIES_PER_PAGE, 40 | self::ENTRIES_PER_PAGE, 41 | $entityName, 42 | function(array $rows) use($sender, $page, $commandLabel, $entityName): void { 43 | $pets = ""; 44 | $index = PetsTopCommand::ENTRIES_PER_PAGE * ($page - 1); 45 | 46 | foreach($rows as [ 47 | "Player" => $player, 48 | "PetName" => $petName, 49 | "EntityName" => $entityName, 50 | "PetLevel" => $petLevel, 51 | "LevelPoints" => $levelPoints 52 | ]) { 53 | $pets .= TextFormat::YELLOW . ++$index . ". " . TextFormat::AQUA . $player . "'s Pet " . $entityName . ", " . TextFormat::YELLOW . $petName; 54 | $pets .= TextFormat::GRAY . "(" . "Lvl " . TextFormat::AQUA . $petLevel . TextFormat::GRAY . ", " . TextFormat::AQUA . $levelPoints . TextFormat::GRAY . " xp)" . TextFormat::EOL; 55 | } 56 | 57 | if($pets === "") { 58 | if($page === 1) { 59 | if($entityName === null) { 60 | $sender->sendMessage(TextFormat::RED . $this->getLoader()->translate("commands.errors.pets.none-on-server")); 61 | } else { 62 | $sender->sendMessage(TextFormat::RED . $this->getLoader()->translate("commands.errors.pets.none-on-server-type", [$entityName])); 63 | } 64 | } else { 65 | if($entityName === null) { 66 | $this->onCommand($sender, $commandLabel, [1]);//send first page. 67 | } else { 68 | $this->onCommand($sender, $commandLabel, [$entityName, 1]);//send first page. 69 | } 70 | } 71 | } else { 72 | $message = TextFormat::GREEN . "--- Pets Leaderboard " . TextFormat::YELLOW . "#" . $page . TextFormat::GREEN . " ---" . TextFormat::EOL; 73 | $message .= $pets; 74 | $sender->sendMessage($message); 75 | } 76 | } 77 | ); 78 | return true; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/RemovePetCommand.php: -------------------------------------------------------------------------------- 1 | [player]", ["rmp"]); 14 | $this->setPermission("blockpets.command.removepet"); 15 | } 16 | 17 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 18 | if(!isset($args[0])) { 19 | return false; 20 | } 21 | 22 | $loader = $this->getLoader(); 23 | if(isset($args[1])) { 24 | if(($player = $loader->getServer()->getPlayerByPrefix($args[1])) === null) { 25 | $this->sendWarning($sender, $loader->translate("commands.errors.player.not-found")); 26 | return true; 27 | } 28 | if(($pet = $loader->getPetByName($args[0], $player->getName())) === null) { 29 | $this->sendWarning($sender, $loader->translate("commands.errors.player.no-pet-other")); 30 | return true; 31 | } 32 | $loader->removePet($pet); 33 | $loader->getDatabase()->unregisterPet($pet); 34 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.removepet.success", [$pet->getPetName()])); 35 | return true; 36 | } 37 | 38 | if(($pet = $loader->getPetByName($args[0])) === null) { 39 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.doesnt-exist")); 40 | return true; 41 | } 42 | 43 | $loader->removePet($pet); 44 | $loader->getDatabase()->unregisterPet($pet); 45 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.removepet.success", [$pet->getPetName()])); 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/SpawnPetCommand.php: -------------------------------------------------------------------------------- 1 | [name] [size] [baby] [player]", ["sp"]); 15 | $this->setPermission("blockpets.command.spawnpet.use"); 16 | } 17 | 18 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 19 | if(!($sender instanceof Player) && count($args) !== 5) { 20 | $this->sendConsoleError($sender); 21 | //$sender->sendMessage(TF::RED . "When using spawnpet from the console, all arguments must be provided."); 22 | return false; 23 | } 24 | 25 | if($sender instanceof Player) { 26 | if(count($args) > 5 || count($args) < 1) { 27 | return false; 28 | } 29 | 30 | if(!$sender->hasPermission("blockpets.pet." . strtolower($args[0]))) { 31 | $this->sendPermissionMessage($sender); 32 | return true; 33 | } 34 | } 35 | 36 | $player = $sender; 37 | $loader = $this->getLoader(); 38 | 39 | if(isset($args[4])) { 40 | if(($player = $loader->getServer()->getPlayerByPrefix($args[4])) === null) { 41 | $this->sendWarning($sender, $loader->translate("commands.errors.player.not-found")); 42 | return true; 43 | } 44 | if(!$sender->hasPermission("blockpets.command.spawnpet.others")) { 45 | $this->sendWarning($sender, $loader->translate("commands.spawnpet.no-permission.others")); 46 | return true; 47 | } 48 | } 49 | 50 | if(!isset($args[1]) || empty(trim($args[1]))) { 51 | /** @phpstan-ignore-next-line */ 52 | $args[1] = $player->getDisplayName(); 53 | } 54 | 55 | if(isset($args[2])) { 56 | if(!is_numeric($args[2])) { 57 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.numeric")); 58 | return true; 59 | } 60 | } 61 | 62 | if(isset($args[3])) { 63 | if($args[3] === "false" || $args[3] === "no") { 64 | $args[3] = false; 65 | } else { 66 | $args[3] = true; 67 | } 68 | } else { 69 | $args[3] = false; 70 | } 71 | $petName = $loader->getPet($args[0]); 72 | if($petName === null) { 73 | $this->sendWarning($sender, $loader->translate("commands.errors.pet.doesnt-exist", [$args[0]])); 74 | return true; 75 | } 76 | if(count($loader->getPetsFrom($player)) >= $loader->getBlockPetsConfig()->getMaxPets() && !$player->hasPermission("blockpets.bypass-limit")) { 77 | $sender->sendMessage($loader->translate("commands.spawnpet.exceeded-limit", [ 78 | $player === $sender ? "You have " : "Your target has " 79 | ])); 80 | return true; 81 | } 82 | if(strtolower($args[1]) === "select") { 83 | if($player !== $sender) { 84 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.spawnpet.selecting-name", [$player->getName()])); 85 | } 86 | $player->sendMessage(TF::GREEN . $loader->translate("commands.spawnpet.name")); 87 | $loader->selectingName[$player->getName()] = [ 88 | "petType" => $petName, 89 | "scale" => isset($args[2]) ? (float) $args[2] : 1.0, 90 | "isBaby" => $args[3] ?? false 91 | ]; 92 | return true; 93 | } 94 | if($loader->getPetByName($args[1], $player->getName()) !== null) { 95 | $this->sendWarning($sender, $loader->translate("commands.errors.player.already-own-pet")); 96 | return true; 97 | } 98 | 99 | $pet = $loader->createPet((string) $petName, $player, $args[1], isset($args[2]) ? (float) $args[2] : 1.0, $args[3]); 100 | if($pet === null) { 101 | $this->sendWarning($sender, $loader->translate("commands.errors.plugin-cancelled")); 102 | return true; 103 | } 104 | $sender->sendMessage(TF::GREEN . $loader->translate("commands.spawnpet.success", [$args[1]])); 105 | $pet->register(); 106 | if($player->getName() !== $sender->getName()) { 107 | $player->sendMessage(TF::GREEN . $loader->translate("commands.spawnpet.success.other", [$args[1]])); 108 | } 109 | return true; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/commands/TogglePetCommand.php: -------------------------------------------------------------------------------- 1 | [player]", ["togglep"]); 15 | $this->setPermission("blockpets.command.togglepet"); 16 | } 17 | 18 | public function onCommand(CommandSender $sender, string $commandLabel, array $args): bool { 19 | $loader = $this->getLoader(); 20 | if(!isset($args[0])) { 21 | $this->sendWarning($sender, TextFormat::RED . $loader->translate("commands.togglepet.no-pet-specified")); 22 | return false; 23 | } 24 | 25 | if(!($sender instanceof Player) && count($args) !== 2) { 26 | $this->sendConsoleError($sender); 27 | return false; 28 | } 29 | 30 | $player = $sender; 31 | if(isset($args[1])) { 32 | if(($player = $loader->getServer()->getPlayerByPrefix($args[1])) === null) { 33 | $this->sendWarning($sender, TextFormat::RED . $loader->translate("commands.errors.player.not-found")); 34 | return true; 35 | } 36 | } 37 | $loader->getDatabase()->togglePets( 38 | $player = $player->getName(), 39 | $type = $args[0] === "all" ? null : $args[0], 40 | function(array $rows) use($loader, $type, $sender, $player): void { 41 | if(empty($rows)) { 42 | $this->sendWarning($sender, TextFormat::RED . $loader->translate("commands.errors.player.no-pet")); 43 | return; 44 | } 45 | 46 | if($type === null) { 47 | $pets = []; 48 | 49 | foreach($rows as [ 50 | "PetName" => $petName, 51 | "Visible" => $isVisible 52 | ]) { 53 | $pet = $loader->getPetByName($petName, $player); 54 | if($pet !== null) { 55 | $pets[$pet->getPetName()] = $isVisible; 56 | $pet->updateVisibility((bool) $isVisible); 57 | } 58 | } 59 | 60 | if(empty($pets)) { 61 | $this->sendWarning($sender, TextFormat::RED . $loader->translate("commands.errors.player.no-pet")); 62 | return; 63 | } 64 | 65 | $visible = array_keys($pets, 1, true); 66 | /** @phpstan-ignore-next-line */ 67 | if(count($visible) === 0) { 68 | $sender->sendMessage(TextFormat::GREEN . $loader->translate( 69 | $sender->getName() === $player ? "commands.togglepet.success" : "commands.togglepet.success-others", 70 | $sender->getName() === $player ? ["off"] : [$player, "off"] 71 | )); 72 | } elseif(count($visible) === count($pets)) { 73 | $sender->sendMessage(TextFormat::GREEN . $loader->translate( 74 | $sender->getName() === $player ? "commands.togglepet.success" : "commands.togglepet.success-others", 75 | $sender->getName() === $player ? ["on"] : [$player, "on"] 76 | )); 77 | } else { 78 | $sender->sendMessage(TextFormat::GREEN . $loader->translate( 79 | $sender->getName() === $player ? "commands.togglepet.success-diff" : "commands.togglepet.success-diff-others", 80 | $sender->getName() === $player ? [implode(", ", $visible)] : [$player, implode(", ", $visible)] 81 | )); 82 | } 83 | } else { 84 | ["PetName" => $petName, "Visible" => $isVisible] = array_pop($rows); 85 | $pet = $loader->getPetByName($petName, $player); 86 | if($pet === null) { 87 | $this->sendWarning($sender, TextFormat::RED . $loader->translate("commands.errors.player.no-pet")); 88 | return; 89 | } 90 | 91 | $pet->updateVisibility((bool) $isVisible); 92 | $sender->sendMessage(TextFormat::GREEN . $loader->translate( 93 | $sender->getName() === $player ? "commands.togglepet.success-specific" : "commands.togglepet.success-specific-others", 94 | $sender->getName() === $player ? [$petName, $isVisible ? "on" : "off"] : [$player, $petName, $isVisible ? "on" : "off"] 95 | )); 96 | } 97 | } 98 | ); 99 | return true; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/configurable/BlockPetsConfig.php: -------------------------------------------------------------------------------- 1 | saveDefaultConfig(); 14 | $this->collectPreferences(); 15 | } 16 | 17 | public function collectPreferences(): void { 18 | $data = yaml_parse_file($this->getLoader()->getDataFolder() . "config.yml"); 19 | $this->settings = $data; 20 | } 21 | 22 | public function getLoader(): Loader { 23 | return $this->loader; 24 | } 25 | 26 | public function doHardReset(): bool { 27 | return (bool) ($this->settings["Hard-Reset"] ?? false); 28 | } 29 | 30 | public function arePetsInvulnerable(): bool { 31 | return (bool) ($this->settings["Invulnerable-Pets"] ?? false); 32 | } 33 | 34 | public function petsDoAttack(): bool { 35 | return (bool) ($this->settings["Attacking-Pets"] ?? true); 36 | } 37 | 38 | public function getBasePetHealth(): int { 39 | return (int) ($this->settings["Pet-Base-Health"] ?? 20); 40 | } 41 | 42 | public function getPetHealthPerLevel(): float { 43 | return (float) ($this->settings["Pet-Per-Level-Health"] ?? 0.5); 44 | } 45 | 46 | public function getPetSizePerLevel(): float { 47 | return (float) ($this->settings["Pet-Per-Level-Size"] ?? 0.03); 48 | } 49 | 50 | public function getBasePetDamage(): int { 51 | return (int) ($this->settings["Pet-Base-Damage"] ?? 4); 52 | } 53 | 54 | public function getPetDamagePerLevel(): float { 55 | return (float) ($this->settings["Pet-Per-Level-Damage"] ?? 0.1); 56 | } 57 | 58 | public function getRespawnTime(): int { 59 | return (int) ($this->settings["Pet-Respawn-Time"] ?? 10); 60 | } 61 | 62 | public function getPlayerExperiencePoints(): int { 63 | return (int) ($this->settings["Experience-Points-Per-Player-Kill"] ?? 15); 64 | } 65 | 66 | public function getEntityExperiencePoints(): int { 67 | return (int) ($this->settings["Experience-Points-Per-Entity-Kill"] ?? 10); 68 | } 69 | 70 | public function getMySQLInfo(): array { 71 | return (array) ($this->settings["MySQL-Info"] ?? []); 72 | } 73 | 74 | public function storeToDatabase(): bool { 75 | return (bool) ($this->settings["Store-To-Database"] ?? true); 76 | } 77 | 78 | public function getDatabase(): string { 79 | return (string) ($this->settings["Database"] ?? "SQLite"); 80 | } 81 | 82 | public function getLanguage(): string { 83 | return (string) ($this->settings["Language"] ?? "en"); 84 | } 85 | 86 | public function getMaxPets(): int { 87 | return (int) ($this->settings["Pet-Limit"] ?? 3); 88 | } 89 | 90 | public function arePetsInvulnerableIfOwnerIs(): bool { 91 | return (bool) ($this->settings["Invulnerable-If-Owner-Is"] ?? true); 92 | } 93 | 94 | public function giveExperienceWhenFed(): bool { 95 | return (bool) ($this->settings["Give-Experience-For-Feeding"] ?? true); 96 | } 97 | 98 | public function shouldStalkPetOwner(): bool { 99 | return (bool) ($this->settings["Stalk-Owner-Blindly"] ?? false); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/configurable/LanguageConfig.php: -------------------------------------------------------------------------------- 1 | collectMessages(); 15 | } 16 | 17 | public function collectMessages(): void { 18 | $languageSelected = false; 19 | $language = []; 20 | foreach($this->getLoader()->getAvailableLanguages() as $availableLanguage) { 21 | if($this->getLoader()->getBlockPetsConfig()->getLanguage() === $availableLanguage) { 22 | $this->getLoader()->saveResource("languages/" . $availableLanguage . ".yml", true); 23 | $language = yaml_parse_file($this->getLoader()->getDataFolder() . "languages/" . $availableLanguage . ".yml"); 24 | $languageSelected = true; 25 | break; 26 | } 27 | } 28 | if(!$languageSelected) { 29 | $this->getLoader()->saveResource("languages/en.yml"); 30 | $language = yaml_parse_file($this->getLoader()->getDataFolder() . "languages/en.yml"); 31 | } 32 | 33 | $iterator = new \RecursiveTreeIterator(new \RecursiveArrayIterator($language)); 34 | $keys = []; 35 | 36 | foreach($iterator as $value) { 37 | $prefix = str_replace("\\", "|", $iterator->getPrefix()); 38 | 39 | $index = strpos($prefix, "|-"); 40 | if($index !== false) { 41 | $index = max(0, $index / 2); 42 | } 43 | 44 | if(count($keys) > $index) { 45 | $keys = array_slice($keys, 0, $index + 1); 46 | } 47 | 48 | $keys[$index] = $iterator->key(); 49 | $entry = $iterator->getEntry(); 50 | 51 | if($entry !== "Array") { 52 | $this->messages[implode(".", $keys)] = TextFormat::colorize($entry); 53 | } 54 | } 55 | } 56 | 57 | public function getLoader(): Loader { 58 | return $this->loader; 59 | } 60 | 61 | public function get(string $key): string { 62 | return $this->messages[$key] ?? $key; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/configurable/PetMessages.php: -------------------------------------------------------------------------------- 1 | getLoader()->getDataFolder() . "pet_messages.yml"); 17 | $this->messages = $data; 18 | } 19 | 20 | public function getLoader(): Loader { 21 | return $this->loader; 22 | } 23 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/configurable/PetProperties.php: -------------------------------------------------------------------------------- 1 | saveResource("pet_properties.yml"); 14 | $this->collectProperties(); 15 | } 16 | 17 | public function collectProperties(): void { 18 | $data = yaml_parse_file($this->getLoader()->getDataFolder() . "pet_properties.yml"); 19 | $this->properties = $data; 20 | } 21 | 22 | public function getLoader(): Loader { 23 | return $this->loader; 24 | } 25 | 26 | public function getPropertiesFor(string $entityType): array { 27 | if(!$this->propertiesExistFor($entityType)) { 28 | return []; 29 | } 30 | return $this->properties[$entityType]; 31 | } 32 | 33 | public function propertiesExistFor(string $entityType): bool { 34 | if(isset($this->properties[$entityType])) { 35 | return true; 36 | } 37 | return false; 38 | } 39 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/BlockPetsEvent.php: -------------------------------------------------------------------------------- 1 | loader; 17 | } 18 | 19 | public function getPlugin(): Loader { 20 | return $this->loader; 21 | } 22 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/PetEvent.php: -------------------------------------------------------------------------------- 1 | pet; 20 | } 21 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/PetInventoryInitializationEvent.php: -------------------------------------------------------------------------------- 1 | pet->getPetOwnerName(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/PetLevelUpEvent.php: -------------------------------------------------------------------------------- 1 | from; 24 | } 25 | 26 | /** 27 | * Returns the pet level AFTER adding the levels. 28 | */ 29 | public function getTo(): int { 30 | return $this->to; 31 | } 32 | 33 | /** 34 | * Sets the pet level AFTER adding points to the given parameter. 35 | */ 36 | public function setTo(int $to): void { 37 | if($to < 1) { 38 | throw new \LogicException("Pet level cannot be negative."); 39 | } 40 | 41 | $this->to = $to; 42 | } 43 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/PetRemoveEvent.php: -------------------------------------------------------------------------------- 1 | pet->getPetOwnerName(); 13 | } 14 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/PetRespawnEvent.php: -------------------------------------------------------------------------------- 1 | petData; 21 | } 22 | 23 | /** 24 | * Returns the delay in seconds for the pet to respawn. 25 | */ 26 | public function getDelay(): int { 27 | return $this->delay; 28 | } 29 | 30 | /** 31 | * Sets the delay for a pet to respawn. 32 | */ 33 | public function setDelay(int $secondsDelay): void { 34 | $this->delay = $secondsDelay; 35 | } 36 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/events/PetSpawnEvent.php: -------------------------------------------------------------------------------- 1 | pet->getPetOwner(); 19 | } 20 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/items/Saddle.php: -------------------------------------------------------------------------------- 1 | getEntity(); 33 | if($player instanceof Player) { 34 | if($event->getCause() === EntityDamageEvent::CAUSE_FALL) { 35 | if($this->getLoader()->isRidingAPet($player)) { 36 | $event->cancel(); 37 | return; 38 | } 39 | } 40 | if($event instanceof EntityDamageByEntityEvent) { 41 | if($event->isCancelled()) { 42 | return; 43 | } 44 | $pets = $this->getLoader()->getPetsFrom($player); 45 | if(!empty($pets)) { 46 | foreach($pets as $pet) { 47 | if(!($pet instanceof IrasciblePet)) { 48 | continue; 49 | } 50 | $attacker = $event->getDamager(); 51 | if($attacker instanceof Living) { 52 | $pet->setAngry($attacker); 53 | } 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | public function getLoader(): Loader { 61 | return $this->loader; 62 | } 63 | 64 | /** 65 | * Used to respawn pets to the player, and fetch pets from the database if this has been configured. 66 | */ 67 | public function onPlayerJoin(PlayerJoinEvent $event): void { 68 | $player = $event->getPlayer(); 69 | $loader = $this->getLoader(); 70 | 71 | if(!$loader->getBlockPetsConfig()->doHardReset()) { 72 | $loader->getDatabase()->load( 73 | $player->getName(), 74 | function(array $petData) use ($player, $loader): void { 75 | $hard_reset = $loader->getBlockPetsConfig()->doHardReset(); 76 | foreach($petData as $data) { 77 | $pet = $loader->createPet( 78 | $data["EntityName"], 79 | $player, 80 | $data["PetName"], 81 | $data["PetSize"], 82 | (bool) $data["IsBaby"], 83 | $data["PetLevel"], 84 | $data["LevelPoints"], 85 | (bool) $data["Chested"], 86 | (bool) $data["Visible"], 87 | $data["Inventory"] 88 | ); 89 | $pet->spawnToAll(); 90 | $pet->setDormant(false); 91 | /** @phpstan-ignore-next-line */ 92 | if($hard_reset) { 93 | $pet->close(); 94 | } 95 | } 96 | } 97 | ); 98 | } 99 | } 100 | 101 | /** 102 | * Used to select a name through chat. Allows for names with spaces and players to choose themselves. 103 | */ 104 | public function onChat(PlayerChatEvent $event): void { 105 | $player = $event->getPlayer(); 106 | $loader = $this->getLoader(); 107 | if(isset($loader->selectingName[$player->getName()])) { 108 | $petName = $event->getMessage(); 109 | $event->cancel(); 110 | if($loader->getPetByName($petName, $player->getName()) !== null) { 111 | $player->sendMessage(TextFormat::RED . "[Warning] You already own a pet with that name. Please choose a different name."); 112 | return; 113 | } 114 | $data = $loader->selectingName[$player->getName()]; 115 | 116 | $loader->createPet($data["petType"], $player, $petName, $data["scale"], $data["isBaby"]); 117 | $player->sendMessage(TextFormat::GREEN . "Successfully obtained a " . $data["petType"] . " with the name " . $event->getMessage()); 118 | unset($loader->selectingName[$player->getName()]); 119 | } 120 | } 121 | 122 | public function onEntitySpawn(EntitySpawnEvent $event): void { 123 | $entity = $event->getEntity(); 124 | if($entity instanceof BasePet) { 125 | $clearLaggPlugin = $this->getLoader()->getServer()->getPluginManager()->getPlugin("ClearLagg"); 126 | if($clearLaggPlugin !== null) { 127 | /** @phpstan-ignore-next-line */ 128 | $clearLaggPlugin->exemptEntity($entity); 129 | } 130 | } 131 | } 132 | 133 | public function onEntityDespawn(EntityDespawnEvent $event): void { 134 | $entity = $event->getEntity(); 135 | if($entity instanceof BasePet) { 136 | if(!$this->loader->getBlockPetsConfig()->storeToDatabase()) { 137 | $this->loader->getDatabase()->unregisterPet($entity); 138 | $this->loader->removePet($entity, false); 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/listeners/RidingListener.php: -------------------------------------------------------------------------------- 1 | getPacket(); 25 | if($packet instanceof PlayerAuthInputPacket) { 26 | $loader = $this->getLoader(); 27 | $player = $event->getOrigin()->getPlayer(); 28 | if($loader->isRidingAPet($player)) { 29 | if(((int)$packet->getMoveVecX()) === 0 && ((int)$packet->getMoveVecZ()) === 0) { 30 | return; 31 | } 32 | $pet = $loader->getRiddenPet($player); 33 | if($pet->isClosed() || $pet->isFlaggedForDespawn()) { 34 | return; 35 | } 36 | $pet->doRidingMovement($packet->getMoveVecX(), $packet->getMoveVecZ()); 37 | } 38 | } elseif($packet instanceof InteractPacket) { 39 | if($packet->action === InteractPacket::ACTION_LEAVE_VEHICLE) { 40 | $loader = $this->getLoader(); 41 | $player = $event->getOrigin()->getPlayer(); 42 | if($loader->isRidingAPet($player)) { 43 | $loader->getRiddenPet($player)->throwRiderOff(); 44 | } 45 | } 46 | } 47 | } 48 | 49 | public function getLoader(): Loader { 50 | return $this->loader; 51 | } 52 | 53 | /** 54 | * Used to dismount the player if it warps, just like it does in vanilla. 55 | */ 56 | public function onTeleport(EntityTeleportEvent $event): void { 57 | $player = $event->getEntity(); 58 | if($player instanceof Player) { 59 | $loader = $this->getLoader(); 60 | if($loader->isRidingAPet($player)) { 61 | $loader->getRiddenPet($player)->throwRiderOff(); 62 | foreach($loader->getPetsFrom($player) as $pet) { 63 | $pet->dismountFromOwner(); 64 | } 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * Used to dismount the player from the ridden pet. This does not matter for the player, but could have a significant effect on the pet's behaviour. 71 | */ 72 | public function onPlayerQuit(PlayerQuitEvent $event): void { 73 | $loader = $this->getLoader(); 74 | foreach($loader->getPetsFrom($event->getPlayer()) as $pet) { 75 | if($pet->isRidden()) { 76 | $pet->throwRiderOff(); 77 | } 78 | $pet->close(); 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/BouncingPet.php: -------------------------------------------------------------------------------- 1 | followRangeSq = 9 + $this->getScale(); 19 | $this->jumpVelocity = $this->jumpHeight * 12 * $this->getScale(); 20 | } 21 | 22 | public function doPetUpdates(int $currentTick): bool { 23 | if(!parent::doPetUpdates($currentTick)) { 24 | return false; 25 | } 26 | 27 | if($this->jumpTicks > 0) { 28 | --$this->jumpTicks; 29 | } 30 | 31 | if(!$this->isOnGround()) { 32 | if($this->motion->y > -$this->gravity * 2) { 33 | $this->motion->y = -$this->gravity * 2; 34 | } else { 35 | $this->motion->y -= $this->gravity; 36 | } 37 | } else { 38 | $this->motion->y -= $this->gravity; 39 | } 40 | 41 | if($this->isRidden()) { 42 | return false; 43 | } 44 | 45 | if($this->isAngry()) { 46 | $this->doAttackingMovement(); 47 | } else { 48 | $this->follow($this->getPetOwner(), $this->xOffset, 0.0, $this->zOffset); 49 | } 50 | 51 | $this->updateMovement(); 52 | return true; 53 | } 54 | 55 | public function doAttackingMovement(): void { 56 | if(!$this->checkAttackRequirements()) { 57 | return; 58 | } 59 | 60 | $target = $this->getTarget(); 61 | $this->follow($target); 62 | 63 | if($this->location->distance($target->location) <= $this->scale + 1 && $this->waitingTime <= 0) { 64 | $event = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $this->getAttackDamage()); 65 | $target->attack($event); 66 | 67 | if(!$event->isCancelled() && !$target->isAlive()) { 68 | if($target instanceof Player) { 69 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getPlayerExperiencePoints()); 70 | } else { 71 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getEntityExperiencePoints()); 72 | } 73 | $this->calmDown(); 74 | } 75 | 76 | $this->waitingTime = 12; 77 | } elseif($this->location->distance($this->getPetOwner()->location) > 25 || $this->location->distance($this->getTarget()->location) > 15) { 78 | $this->calmDown(); 79 | } 80 | 81 | --$this->waitingTime; 82 | } 83 | 84 | public function jump(): void { 85 | parent::jump(); 86 | $this->jumpTicks = 10; 87 | } 88 | 89 | public function doRidingMovement(float $motionX, float $motionZ): void { 90 | $rider = $this->getPetOwner(); 91 | 92 | $this->location->pitch = $rider->location->pitch; 93 | $this->location->yaw = $rider->location->yaw; 94 | 95 | $speed_factor = 2 * $this->getSpeed(); 96 | $direction_plane = $this->getDirectionPlane(); 97 | $x = $direction_plane->x / $speed_factor; 98 | $z = $direction_plane->y / $speed_factor; 99 | 100 | if($this->jumpTicks > 0) { 101 | $this->jumpTicks--; 102 | } 103 | 104 | if(!$this->isOnGround()) { 105 | if($this->motion->y > -$this->gravity * 2) { 106 | $this->motion->y = -$this->gravity * 2; 107 | } else { 108 | $this->motion->y -= $this->gravity; 109 | } 110 | } else { 111 | $this->motion->y -= $this->gravity; 112 | } 113 | 114 | $finalMotionX = 0; 115 | $finalMotionZ = 0; 116 | 117 | switch($motionZ) { 118 | case 1: 119 | $finalMotionX = $x; 120 | $finalMotionZ = $z; 121 | if($this->isOnGround()) { 122 | $this->jump(); 123 | } 124 | break; 125 | case 0: 126 | if($this->isOnGround()) { 127 | $this->jump(); 128 | } 129 | break; 130 | case -1: 131 | $finalMotionX = -$x; 132 | $finalMotionZ = -$z; 133 | if($this->isOnGround()) { 134 | $this->jump(); 135 | } 136 | break; 137 | default: 138 | $average = $x + $z / 2; 139 | $finalMotionX = $average / 1.414 * $motionZ; 140 | $finalMotionZ = $average / 1.414 * $motionX; 141 | if($this->isOnGround()) { 142 | $this->jump(); 143 | } 144 | break; 145 | } 146 | 147 | switch($motionX) { 148 | case 1: 149 | $finalMotionX = $z; 150 | $finalMotionZ = -$x; 151 | if($this->isOnGround()) { 152 | $this->jump(); 153 | } 154 | break; 155 | case 0: 156 | if($this->isOnGround()) { 157 | $this->jump(); 158 | } 159 | break; 160 | case -1: 161 | $finalMotionX = -$z; 162 | $finalMotionZ = $x; 163 | if($this->isOnGround()) { 164 | $this->jump(); 165 | } 166 | break; 167 | } 168 | 169 | $this->move($finalMotionX, $this->motion->y, $finalMotionZ); 170 | $this->updateMovement(); 171 | } 172 | 173 | public function attack(EntityDamageEvent $source): void { 174 | if($source->getCause() === $source::CAUSE_FALL) { 175 | $source->cancel(); 176 | } 177 | parent::attack($source); 178 | } 179 | 180 | public function useProperties(array $properties): void { 181 | parent::useProperties($properties); 182 | $this->jumpHeight = (float) $properties["Jumping-Height"]; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/Calculator.php: -------------------------------------------------------------------------------- 1 | recalculateHealth(); 18 | $this->recalculateSize(); 19 | $this->recalculateDamage(); 20 | $this->updateNameTag(); 21 | } 22 | 23 | /** 24 | * Recalculates maximum health that the pet should have according to its configuration scalings. 25 | */ 26 | public function recalculateHealth(): void { 27 | $pet = $this->getPet(); 28 | $bpConfig = $pet->getLoader()->getBlockPetsConfig(); 29 | 30 | $petLevel = $pet->getPetLevel(); 31 | $baseHealth = $bpConfig->getBasePetHealth(); 32 | $scalingHealth = $bpConfig->getPetHealthPerLevel(); 33 | 34 | $pet->setMaxHealth((int) floor($baseHealth + $scalingHealth * $petLevel)); 35 | $pet->fullHeal(); 36 | } 37 | 38 | /** 39 | * @return BasePet 40 | */ 41 | public function getPet(): BasePet { 42 | return $this->pet; 43 | } 44 | 45 | /** 46 | * Recalculates size that the pet should have according to its configuration scalings. 47 | */ 48 | public function recalculateSize(): bool { 49 | $pet = $this->getPet(); 50 | $petOwner = $pet->getPetOwner(); 51 | if($petOwner === null) { 52 | return false; 53 | } 54 | 55 | if($pet->getScale() > $pet->getMaxSize() && !($petOwner->hasPermission("blockpets.bypass-size-limit"))) { 56 | $pet->setScale($pet->getMaxSize()); 57 | } else { 58 | $petLevel = $pet->getPetLevel(); 59 | $scalingSize = $pet->getLoader()->getBlockPetsConfig()->getPetSizePerLevel(); 60 | $pet->setScale((float) ($pet->getStartingScale() + $scalingSize * $petLevel)); 61 | } 62 | return true; 63 | } 64 | 65 | /** 66 | * Recalculates attack damage that the pet should have according to its configuration attack damage. 67 | */ 68 | public function recalculateDamage(): void { 69 | $pet = $this->getPet(); 70 | $bpConfig = $pet->getLoader()->getBlockPetsConfig(); 71 | 72 | $petLevel = $pet->getPetLevel(); 73 | $baseDamage = $bpConfig->getBasePetDamage(); 74 | $scalingDamage = $bpConfig->getPetDamagePerLevel(); 75 | 76 | $pet->setAttackDamage((int) round($baseDamage + $scalingDamage * $petLevel)); 77 | } 78 | 79 | /** 80 | * Updates the name tag to include the latest data like level, level points etc. 81 | */ 82 | public function updateNameTag(): void { 83 | $pet = $this->getPet(); 84 | $percentage = round($pet->getPetLevelPoints() / LevelCalculator::getRequiredLevelPoints($pet->getPetLevel()) * 100, 1); 85 | $pet->setNameTag( 86 | $pet->getPetName() . "\n" . 87 | TextFormat::GRAY . "Lvl." . TextFormat::AQUA . $pet->getPetLevel() . TextFormat::GRAY . " (" . TextFormat::YELLOW . $percentage . TextFormat::GRAY . "%) " . TextFormat::GRAY . $pet->getName() . 88 | TextFormat::RED . " (" . $pet->getHealth() . "/" . $pet->getMaxHealth() . ")" 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/HoveringPet.php: -------------------------------------------------------------------------------- 1 | followRangeSq = 8 + $this->getScale(); 27 | } 28 | 29 | public function doPetUpdates(int $currentTick): bool { 30 | if(!parent::doPetUpdates($currentTick)) { 31 | return false; 32 | } 33 | 34 | if($this->isRidden()) { 35 | return false; 36 | } 37 | 38 | if($this->isAngry()) { 39 | $this->doAttackingMovement(); 40 | } else { 41 | $this->follow($this->getPetOwner(), $this->xOffset, abs($this->yOffset) + 1.5, $this->zOffset); 42 | } 43 | 44 | $this->updateMovement(); 45 | return true; 46 | } 47 | 48 | public function follow(Entity $target, float $xOffset = 0.0, float $yOffset = 0.0, float $zOffset = 0.0): void { 49 | $targetLoc = $target->getLocation(); 50 | $currLoc = $this->getLocation(); 51 | 52 | $x = $targetLoc->getX() + $xOffset - $currLoc->getX(); 53 | $y = $targetLoc->getY() + $yOffset - $currLoc->getY(); 54 | $z = $targetLoc->getZ() + $zOffset - $currLoc->getZ(); 55 | 56 | $xz_sq = $x * $x + $z * $z; 57 | $xz_modulus = sqrt($xz_sq); 58 | 59 | if($xz_sq < $this->followRangeSq) { 60 | $this->motion->x = 0; 61 | $this->motion->z = 0; 62 | } else { 63 | $speed_factor = $this->getSpeed() * 0.15; 64 | $this->motion->x = $speed_factor * ($x / $xz_modulus); 65 | $this->motion->z = $speed_factor * ($z / $xz_modulus); 66 | } 67 | 68 | if((float) $y !== 0.0) { 69 | $this->motion->y = $this->getSpeed() * 0.25 * $y; 70 | } 71 | 72 | $this->location->yaw = rad2deg(atan2(-$x, $z)); 73 | $this->location->pitch = rad2deg(-atan2($y, $xz_modulus)); 74 | 75 | if(static::getNetworkTypeId() === EntityIds::ENDER_DRAGON) { 76 | $this->location->yaw += 180; 77 | } 78 | 79 | $this->move($this->motion->x, $this->motion->y, $this->motion->z); 80 | } 81 | 82 | public function doAttackingMovement(): void { 83 | if(!$this->checkAttackRequirements()) { 84 | return; 85 | } 86 | 87 | $target = $this->getTarget(); 88 | $this->follow($target, 0.0, 0.5, 0.0); 89 | 90 | if($this->location->distance($target->location) <= $this->scale + 1.1 && $this->waitingTime <= 0 && $target->isAlive()) { 91 | $event = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $this->getAttackDamage()); 92 | $target->attack($event); 93 | 94 | if(!$event->isCancelled() && !$target->isAlive()) { 95 | if($target instanceof Player) { 96 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getPlayerExperiencePoints()); 97 | } else { 98 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getEntityExperiencePoints()); 99 | } 100 | $this->calmDown(); 101 | } 102 | 103 | $this->waitingTime = 12; 104 | } elseif($this->location->distance($this->getPetOwner()->location) > 25 || $this->location->distance($this->getTarget()->location) > 15) { 105 | $this->calmDown(); 106 | } 107 | 108 | --$this->waitingTime; 109 | } 110 | 111 | public function doRidingMovement(float $motionX, float $motionZ): void { 112 | $rider = $this->getPetOwner(); 113 | 114 | $this->location->pitch = $rider->location->pitch; 115 | $this->location->yaw = $this instanceof EnderDragonPet ? $rider->location->yaw + 180 : $rider->location->yaw; 116 | 117 | $speed_factor = 2 * $this->getSpeed(); 118 | $rider_directionvec = $rider->getDirectionVector(); 119 | $x = $rider_directionvec->x / $speed_factor; 120 | $z = $rider_directionvec->z / $speed_factor; 121 | $y = $rider_directionvec->y / $speed_factor; 122 | 123 | $finalMotionX = 0; 124 | $finalMotionZ = 0; 125 | 126 | switch($motionZ) { 127 | case 1: 128 | $finalMotionX = $x; 129 | $finalMotionZ = $z; 130 | break; 131 | case 0: 132 | break; 133 | case -1: 134 | $finalMotionX = -$x; 135 | $finalMotionZ = -$z; 136 | break; 137 | default: 138 | $average = $x + $z / 2; 139 | $finalMotionX = $average / 1.414 * $motionZ; 140 | $finalMotionZ = $average / 1.414 * $motionX; 141 | break; 142 | } 143 | 144 | switch($motionX) { 145 | case 1: 146 | $finalMotionX = $z; 147 | $finalMotionZ = -$x; 148 | break; 149 | case 0: 150 | break; 151 | case -1: 152 | $finalMotionX = -$z; 153 | $finalMotionZ = $x; 154 | break; 155 | } 156 | 157 | if(((float) $y) !== 0.0) { 158 | if($y < 0) { 159 | $this->motion->y = $this->getSpeed() * 0.3 * $y; 160 | } elseif($this->location->y - $this->getWorld()->getHighestBlockAt((int) $this->location->x, (int) $this->location->z) < $this->flyHeight) { 161 | $this->motion->y = $this->getSpeed() * 0.3 * $y; 162 | } 163 | } 164 | if(abs($y) < 0.2) { 165 | $this->motion->y = 0; 166 | } 167 | 168 | $this->move($finalMotionX, $this->motion->y, $finalMotionZ); 169 | $this->updateMovement(); 170 | } 171 | 172 | public function attack(EntityDamageEvent $source): void { 173 | if($source->getCause() === $source::CAUSE_FALL) { 174 | $source->cancel(); 175 | } 176 | parent::attack($source); 177 | } 178 | 179 | public function useProperties(array $properties): void { 180 | parent::useProperties($properties); 181 | $this->flyHeight = (float) $properties["Flying-Height"]; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/IrasciblePet.php: -------------------------------------------------------------------------------- 1 | closed || !$this->isAlive()) { 26 | return; 27 | } 28 | $bpConfig = $this->getLoader()->getBlockPetsConfig(); 29 | if($bpConfig->arePetsInvulnerable()) { 30 | $source->cancel(); 31 | } 32 | if($this->isRidden() && $source->getCause() === $source::CAUSE_FALL) { 33 | $source->cancel(); 34 | } 35 | if($bpConfig->arePetsInvulnerableIfOwnerIs() && ($petOwner = $this->getPetOwner()) !== null) { 36 | $ownerDamageEvent = new EntityDamageEvent($petOwner, EntityDamageEvent::CAUSE_CUSTOM, 0); 37 | $ownerDamageEvent->call(); 38 | if($ownerDamageEvent->isCancelled()) { 39 | $source->cancel(); 40 | } 41 | $ownerDamageEvent->cancel(); 42 | } 43 | if($source instanceof EntityDamageByEntityEvent) { 44 | $attacker = $source->getDamager(); 45 | if(!$bpConfig->arePetsInvulnerable()) { 46 | if($attacker instanceof Player) { 47 | $nameTag = $attacker->getName(); 48 | } else { 49 | $nameTag = $attacker->getNameTag(); 50 | } 51 | if($nameTag === $this->getPetOwnerName()) { 52 | $source->cancel(); 53 | } 54 | if($bpConfig->petsDoAttack() && $attacker instanceof Living && !$source->isCancelled()) { 55 | $this->setAngry($attacker); 56 | } 57 | } 58 | if($attacker instanceof Player && !$attacker->isSneaking() && $this->canBeRidden && $attacker->getName() === $this->getPetOwnerName()) { 59 | $item = $attacker->getInventory()->getItemInHand(); 60 | if($item->getId() === ItemIds::SADDLE) { 61 | $this->setRider($attacker); 62 | $attacker->sendTip(TextFormat::GRAY . "Crouch or jump to dismount..."); 63 | $source->cancel(); 64 | } 65 | } 66 | } 67 | parent::attack($source); 68 | } 69 | 70 | /** 71 | * Sets the pet angry and it's target to the given entity, making it chase the entity. 72 | */ 73 | public function setAngry(Living $entity): bool { 74 | if(!$this->canAttack) { 75 | return false; 76 | } 77 | $this->target = $entity; 78 | if($this instanceof ArrowPet) { 79 | $this->setCritical(); 80 | } 81 | return true; 82 | } 83 | 84 | public function doAttackingMovement(): void { 85 | if(!$this->checkAttackRequirements()) { 86 | return; 87 | } 88 | 89 | $target = $this->getTarget(); 90 | $this->follow($target); 91 | 92 | if($this->location->distance($target->location) <= $this->scale + 0.5 && $this->waitingTime <= 0) { 93 | $event = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $this->getAttackDamage()); 94 | $target->attack($event); 95 | 96 | if(!$event->isCancelled() && !$target->isAlive()) { 97 | if($target instanceof Player) { 98 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getPlayerExperiencePoints()); 99 | } else { 100 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getEntityExperiencePoints()); 101 | } 102 | $this->calmDown(); 103 | } 104 | 105 | $this->waitingTime = 12; 106 | } elseif($this->location->distance($this->getPetOwner()->location) > 25 || $this->location->distance($target->location) > 15) { 107 | $this->calmDown(); 108 | } 109 | 110 | --$this->waitingTime; 111 | } 112 | 113 | public function follow(Entity $target, float $xOffset = 0.0, float $yOffset = 0.0, float $zOffset = 0.0): void { 114 | $targetLoc = $target->getLocation(); 115 | $currLoc = $this->getLocation(); 116 | 117 | $x = $targetLoc->getX() + $xOffset - $currLoc->getX(); 118 | $y = $targetLoc->getY() + $yOffset - $currLoc->getY(); 119 | $z = $targetLoc->getZ() + $zOffset - $currLoc->getZ(); 120 | 121 | $xz_sq = $x * $x + $z * $z; 122 | $xz_modulus = sqrt($xz_sq); 123 | 124 | if($xz_sq < $this->followRangeSq) { 125 | $this->motion->x = 0; 126 | $this->motion->z = 0; 127 | } else { 128 | $speed_factor = $this->getSpeed() * 0.15; 129 | $this->motion->x = $speed_factor * ($x / $xz_modulus); 130 | $this->motion->z = $speed_factor * ($z / $xz_modulus); 131 | } 132 | $this->location->yaw = rad2deg(atan2(-$x, $z)); 133 | $this->location->pitch = rad2deg(-atan2($y, $xz_modulus)); 134 | 135 | $this->move($this->motion->x, $this->motion->y, $this->motion->z); 136 | } 137 | 138 | protected function checkAttackRequirements(): bool { 139 | if($this->closed || !($this->isAlive()) || !($this->isAngry()) || $this->isFlaggedForDespawn()) { 140 | $this->calmDown(); 141 | return false; 142 | } 143 | if(!$this->getTarget()->isAlive()) { 144 | $this->calmDown(); 145 | return false; 146 | } 147 | return true; 148 | } 149 | 150 | /** 151 | * Returns whether this pet is angry or not. 152 | */ 153 | public function isAngry(): bool { 154 | return $this->target !== null; 155 | } 156 | 157 | /** 158 | * Calms down the pet, making it stop chasing it's target. 159 | */ 160 | public function calmDown(): void { 161 | $this->target = null; 162 | if($this instanceof ArrowPet) { 163 | $this->setCritical(false); 164 | } 165 | } 166 | 167 | /** 168 | * Returns the current target of this pet. 169 | */ 170 | public function getTarget(): ?Living { 171 | return $this->target; 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/LevelCalculator.php: -------------------------------------------------------------------------------- 1 | ($req = self::getRequiredLevelPoints($level + $levelled))) { 27 | ++$levelled; 28 | $remaining -= $req; 29 | } 30 | 31 | return $levelled; 32 | } 33 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/PetData.php: -------------------------------------------------------------------------------- 1 | petId; 27 | } 28 | 29 | public function getOwnerName(): string { 30 | return $this->ownerName; 31 | } 32 | 33 | public function getOwner(): ?Player { 34 | return Server::getInstance()->getPlayerExact($this->ownerName); 35 | } 36 | 37 | public function getPetName(): string { 38 | return $this->petName; 39 | } 40 | 41 | public function setPetName(string $petName): PetData { 42 | $this->petName = $petName; 43 | return $this; 44 | } 45 | 46 | public function getScale(): float { 47 | return $this->scale; 48 | } 49 | 50 | public function setScale(float $scale): PetData { 51 | $this->scale = $scale; 52 | return $this; 53 | } 54 | 55 | public function isBaby(): bool { 56 | return $this->isBaby; 57 | } 58 | 59 | public function setBaby(bool $isBaby): PetData { 60 | $this->isBaby = $isBaby; 61 | return $this; 62 | } 63 | 64 | public function getLevel(): int { 65 | return $this->level; 66 | } 67 | 68 | public function setLevel(int $level): PetData { 69 | $this->level = $level; 70 | return $this; 71 | } 72 | 73 | public function getLevelPoints(): int { 74 | return $this->levelPoints; 75 | } 76 | 77 | public function setLevelPoints(int $levelPoints): PetData { 78 | $this->levelPoints = $levelPoints; 79 | return $this; 80 | } 81 | 82 | public function isChested(): bool { 83 | return $this->chested; 84 | } 85 | 86 | public function setChested(bool $chested): PetData { 87 | $this->chested = $chested; 88 | return $this; 89 | } 90 | 91 | public function isVisible(): bool { 92 | return $this->isVisible; 93 | } 94 | 95 | public function setVisible(bool $isVisible): PetData { 96 | $this->isVisible = $isVisible; 97 | return $this; 98 | } 99 | 100 | public function getInventory(): ?string { 101 | return $this->inventory; 102 | } 103 | 104 | public function setInventory(?string $inventory): PetData { 105 | $this->inventory = $inventory; 106 | return $this; 107 | } 108 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/PetIds.php: -------------------------------------------------------------------------------- 1 | isAngry() && $this->isUnderwater()) { 26 | $petOwner = $this->getPetOwner(); 27 | $ownerLoc = $petOwner->getLocation(); 28 | $currLoc = $this->getLocation(); 29 | 30 | $x = $ownerLoc->getX() + $this->xOffset - $currLoc->getX(); 31 | $y = $ownerLoc->getY() + $this->yOffset - $currLoc->getY(); 32 | $z = $ownerLoc->getZ() + $this->zOffset - $currLoc->getZ(); 33 | 34 | $xz_sq = $x * $x + $z * $z; 35 | $xz_modulus = sqrt($xz_sq); 36 | $speed_factor = $this->getSwimmingSpeed() * 0.25; 37 | 38 | if($xz_sq < 6 + $this->getScale()) { 39 | $this->motion->x = 0; 40 | $this->motion->z = 0; 41 | } else { 42 | $this->motion->x = $speed_factor * ($x / $xz_modulus); 43 | $this->motion->z = $speed_factor * ($z / $xz_modulus); 44 | } 45 | $this->motion->y = $speed_factor * $y; 46 | $this->location->yaw = rad2deg(atan2(-$x, $z)); 47 | $this->location->pitch = rad2deg(-atan2($y, $xz_modulus)); 48 | 49 | $this->move($this->motion->x, $this->motion->y, $this->motion->z); 50 | 51 | $this->updateMovement(); 52 | return true; 53 | } 54 | 55 | return true; 56 | } 57 | 58 | public function follow(Entity $target, float $xOffset = 0.0, float $yOffset = 0.0, float $zOffset = 0.0): void { 59 | $targetLoc = $target->getLocation(); 60 | $currLoc = $this->getLocation(); 61 | 62 | $x = $targetLoc->getX() + $xOffset - $currLoc->getX(); 63 | $y = $targetLoc->getY() + $yOffset - $currLoc->getY(); 64 | $z = $targetLoc->getZ() + $zOffset - $currLoc->getZ(); 65 | 66 | $xz_sq = $x * $x + $z * $z; 67 | $xz_modulus = sqrt($xz_sq); 68 | 69 | if($xz_sq < $this->followRangeSq) { 70 | $this->motion->x = 0; 71 | $this->motion->z = 0; 72 | } else { 73 | $speed_factor = $this->getSwimmingSpeed() * 0.15; 74 | $this->motion->x = $speed_factor * ($x / $xz_modulus); 75 | $this->motion->z = $speed_factor * ($z / $xz_modulus); 76 | } 77 | 78 | if($y !== 0.0) { 79 | $this->motion->y = $this->getSwimmingSpeed() * 0.15 * $y; 80 | } 81 | 82 | $this->location->yaw = rad2deg(atan2(-$x, $z)); 83 | $this->location->pitch = rad2deg(-atan2($y, $xz_modulus)); 84 | 85 | $this->move($this->motion->x, $this->motion->y, $this->motion->z); 86 | } 87 | 88 | public function doAttackingMovement(): void { 89 | if(!$this->checkAttackRequirements()) { 90 | return; 91 | } 92 | 93 | if($this->isUnderwater()) { 94 | $target = $this->getTarget(); 95 | $this->follow($target); 96 | 97 | if($this->location->distance($target->location) <= $this->scale + 0.5 && $this->waitingTime <= 0) { 98 | $event = new EntityDamageByEntityEvent($this, $target, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $this->getAttackDamage()); 99 | $target->attack($event); 100 | 101 | if(!$event->isCancelled() && !$target->isAlive()) { 102 | if($target instanceof Player) { 103 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getPlayerExperiencePoints()); 104 | } else { 105 | $this->addPetLevelPoints($this->getLoader()->getBlockPetsConfig()->getEntityExperiencePoints()); 106 | } 107 | $this->calmDown(); 108 | } 109 | 110 | $this->waitingTime = 12; 111 | } elseif($this->location->distance($this->getPetOwner()->location) > 25 || $this->location->distance($target->location) > 15) { 112 | $this->calmDown(); 113 | } 114 | 115 | --$this->waitingTime; 116 | return; 117 | } 118 | 119 | parent::doAttackingMovement(); 120 | } 121 | 122 | public function getSwimmingSpeed(): float { 123 | return $this->swimmingSpeed; 124 | } 125 | 126 | public function doRidingMovement(float $motionX, float $motionZ): void { 127 | if($this->isUnderwater()) { 128 | $rider = $this->getRider(); 129 | 130 | $this->location->pitch = $rider->location->pitch; 131 | $this->location->yaw = $rider->location->yaw; 132 | 133 | $speed_factor = 2 * $this->getSwimmingSpeed(); 134 | $rider_directionvec = $rider->getDirectionVector(); 135 | $x = $rider_directionvec->x / $speed_factor; 136 | $z = $rider_directionvec->z / $speed_factor; 137 | $y = $rider_directionvec->y / $speed_factor; 138 | 139 | $finalMotionX = 0; 140 | $finalMotionZ = 0; 141 | 142 | switch($motionZ) { 143 | case 1: 144 | $finalMotionX = $x; 145 | $finalMotionZ = $z; 146 | break; 147 | case 0: 148 | break; 149 | case -1: 150 | $finalMotionX = -$x; 151 | $finalMotionZ = -$z; 152 | break; 153 | default: 154 | $average = $x + $z / 2; 155 | $finalMotionX = $average / 1.414 * $motionZ; 156 | $finalMotionZ = $average / 1.414 * $motionX; 157 | break; 158 | } 159 | 160 | switch($motionX) { 161 | case 1: 162 | $finalMotionX = $z; 163 | $finalMotionZ = -$x; 164 | break; 165 | case 0: 166 | break; 167 | case -1: 168 | $finalMotionX = -$z; 169 | $finalMotionZ = $x; 170 | break; 171 | } 172 | 173 | if(((float) $y) !== 0.0) { 174 | $this->motion->y = $this->getSwimmingSpeed() * 0.25 * $y; 175 | } 176 | if(abs($y) < 0.1) { 177 | $this->motion->y = 0; 178 | } 179 | $this->move($finalMotionX, $this->motion->y, $finalMotionZ); 180 | 181 | $this->updateMovement(); 182 | return; 183 | } 184 | 185 | parent::doRidingMovement($motionX, $motionZ); 186 | } 187 | 188 | public function useProperties(array $properties): void { 189 | parent::useProperties($properties); 190 | $this->swimmingSpeed = (float) $properties["Swimming-Speed"]; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/WalkingPet.php: -------------------------------------------------------------------------------- 1 | jumpVelocity = $this->gravity * 10; 15 | } 16 | 17 | public function doPetUpdates(int $currentTick): bool { 18 | if(!parent::doPetUpdates($currentTick)) { 19 | return false; 20 | } 21 | 22 | if($this->jumpTicks > 0) { 23 | --$this->jumpTicks; 24 | } 25 | 26 | if(!$this->isOnGround()) { 27 | if($this->motion->y > -$this->gravity * 4) { 28 | $this->motion->y = -$this->gravity * 4; 29 | } else { 30 | $this->motion->y += $this->isUnderwater() ? $this->gravity : -$this->gravity; 31 | } 32 | } else { 33 | if($this->isCollidedHorizontally && $this->jumpTicks === 0) { 34 | $this->jump(); 35 | } else { 36 | $this->motion->y -= $this->gravity; 37 | } 38 | } 39 | 40 | if($this->isRidden()) { 41 | return false; 42 | } 43 | 44 | if($this->isAngry()) { 45 | $this->doAttackingMovement(); 46 | } else { 47 | $this->follow($this->getPetOwner(), $this->xOffset, 0.0, $this->zOffset); 48 | } 49 | 50 | $this->updateMovement(); 51 | return true; 52 | } 53 | 54 | public function jump(): void { 55 | parent::jump(); 56 | $this->jumpTicks = 5; 57 | } 58 | 59 | public function doRidingMovement(float $motionX, float $motionZ): void { 60 | $rider = $this->getPetOwner(); 61 | 62 | $this->location->pitch = $rider->location->pitch; 63 | $this->location->yaw = $rider->location->yaw; 64 | 65 | $speed_factor = 2.5 * $this->getSpeed(); 66 | $direction_plane = $this->getDirectionPlane(); 67 | $x = $direction_plane->x / $speed_factor; 68 | $z = $direction_plane->y / $speed_factor; 69 | 70 | switch($motionZ) { 71 | case 1: 72 | $finalMotionX = $x; 73 | $finalMotionZ = $z; 74 | break; 75 | case -1: 76 | $finalMotionX = -$x; 77 | $finalMotionZ = -$z; 78 | break; 79 | default: 80 | $average = $x + $z / 2; 81 | $finalMotionX = $average / 1.414 * $motionZ; 82 | $finalMotionZ = $average / 1.414 * $motionX; 83 | break; 84 | } 85 | 86 | switch($motionX) { 87 | case 1: 88 | $finalMotionX = $z; 89 | $finalMotionZ = -$x; 90 | break; 91 | case -1: 92 | $finalMotionX = -$z; 93 | $finalMotionZ = $x; 94 | break; 95 | } 96 | 97 | $this->move($finalMotionX, $this->motion->y, $finalMotionZ); 98 | $this->updateMovement(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/AllayPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setGenericFlag(EntityMetadataFlags::CRITICAL, $value); 23 | } 24 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/AxolotlPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, true); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/ChickenPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setGenericFlag(EntityMetadataFlags::ELDER, true); 27 | } 28 | 29 | public function attack(EntityDamageEvent $source): void { 30 | if($source instanceof EntityDamageByEntityEvent) { 31 | $attacker = $source->getDamager(); 32 | if($attacker instanceof Player && random_int(0, 1)) { 33 | $attacker->getNetworkSession()->sendDataPacket(LevelEventPacket::create(2006, 0, $attacker->getLocation())); 34 | } 35 | } 36 | parent::attack($source); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/EnderCrystalPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setGenericFlag(EntityMetadataFlags::EVOKER_SPELL, (bool) $isCasting); 23 | } 24 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/FoxPet.php: -------------------------------------------------------------------------------- 1 | setVariant($this->getRandomType(), $this->getRandomColor()); 38 | } 39 | 40 | public function getRandomType(): int { 41 | return array_rand([ 42 | self::TYPE_NONE, 43 | self::TYPE_WHITE, 44 | self::TYPE_WHITE_FIELD, 45 | self::TYPE_WHITE_DOTS, 46 | self::TYPE_BLACK_DOTS 47 | ]); 48 | } 49 | 50 | public function getRandomColor(): int { 51 | return array_rand([ 52 | self::COLOR_WHITE, 53 | self::COLOR_CREAMY, 54 | self::COLOR_CHESTNUT, 55 | self::COLOR_BROWN, 56 | self::COLOR_BLACK, 57 | self::COLOR_GRAY, 58 | self::COLOR_DARKBROWN 59 | ]); 60 | } 61 | 62 | public function getVariant(): int { 63 | return $this->variant; 64 | } 65 | 66 | public function setVariant(int $type, int $colour): void { 67 | $this->variant = $colour | $type << 8; 68 | $this->getNetworkProperties()->setInt(EntityMetadataProperties::VARIANT, $colour | $type << 8); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/HuskPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setInt(EntityMetadataProperties::VARIANT, $randomVariant); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/MagmaCubePet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setInt(EntityMetadataProperties::VARIANT, $randomVariant); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/PigPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setInt(EntityMetadataProperties::VARIANT, $randomVariant); 28 | } 29 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/SheepPet.php: -------------------------------------------------------------------------------- 1 | setColor(random_int(0, 15)); 23 | } 24 | 25 | public function setColor(int $color): void { 26 | $this->color = $color % 16; 27 | $this->getNetworkProperties()->setByte(EntityMetadataProperties::COLOR, $color % 16); 28 | } 29 | 30 | public function getColor(): int { 31 | return $this->color; 32 | } 33 | 34 | public function doPetUpdates(int $currentTick): bool { 35 | if($currentTick % 10 === 0 && $this->getPetName() === "jeb_") { 36 | $this->setColor($this->getColor() + 1); 37 | } 38 | return parent::doPetUpdates($currentTick); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/SilverFishPet.php: -------------------------------------------------------------------------------- 1 | getPetName() !== "shoghicp") { 22 | return; 23 | } 24 | $this->getNetworkProperties()->setGenericFlag(EntityMetadataFlags::SHEARED, true); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/SpiderPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setGenericFlag(EntityMetadataFlags::CAN_CLIMB, true); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/SquidPet.php: -------------------------------------------------------------------------------- 1 | canCollide = false; 22 | } 23 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/VillagerPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setInt(EntityMetadataProperties::VARIANT, $randomVariant); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/VindicatorPet.php: -------------------------------------------------------------------------------- 1 | getNetworkProperties()->setLong(EntityMetadataProperties::OWNER_EID, $eid); 25 | $this->getNetworkProperties()->setByte(EntityMetadataProperties::COLOR, $randomColour); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/creatures/ZombieHorsePet.php: -------------------------------------------------------------------------------- 1 | prepare(); 13 | if($loader->getBlockPetsConfig()->doHardReset()) { 14 | $this->reset(); 15 | $this->getLoader()->getConfig()->set("Hard-Reset", false); 16 | } 17 | } 18 | 19 | /** 20 | * Called during class construction to let 21 | * databases create and initialize their 22 | * instances. 23 | */ 24 | protected abstract function prepare(): void; 25 | 26 | /** 27 | * Called when the plugin updates so database 28 | * can perform patches (if any). 29 | */ 30 | public abstract function patch(string $version): void; 31 | 32 | protected function getLoader(): Loader { 33 | return $this->loader; 34 | } 35 | 36 | /** 37 | * Resets all values in the database. 38 | */ 39 | protected abstract function reset(): void; 40 | 41 | /** 42 | * Registers pet to the database. 43 | * If the pet's entry already exists in the 44 | * database, the database will perform an 45 | * UPDATE-ALL-VALUES instead. 46 | */ 47 | public abstract function registerPet(BasePet $pet): void; 48 | 49 | /** 50 | * Deletes the pet's entry from the database 51 | * if exists. 52 | */ 53 | public abstract function unregisterPet(BasePet $pet): void; 54 | 55 | /** 56 | * Updates pet's level and level points if it's 57 | * entry exists in the database. 58 | */ 59 | public abstract function updateExperience(BasePet $pet): void; 60 | 61 | /** 62 | * Retrieves all of the owner's pets from the 63 | * database and then calls the callable to 64 | * initialize the fetched entries. 65 | */ 66 | public abstract function load(string $ownerName, callable $callable): void; 67 | 68 | /** 69 | * Fetches all pets' names of the specified player 70 | * from the database and calls the callable to get 71 | * the list of pet names. 72 | * If $entityName is not null, only entities with the 73 | * specified entity name will be fetched. 74 | */ 75 | public abstract function getPlayerPets(string $ownerName, ?string $entityName = null, callable $callable = null): void; 76 | 77 | /** 78 | * Fetches all pets sorted by their level and points 79 | * and calls the callable to get the list of sorted 80 | * pets. 81 | * If $entityName is not null, only entities with the 82 | * specified entity name will be fetched. 83 | */ 84 | public abstract function getPetsLeaderboard(int $offset = 0, int $length = 1, ?string $entityName = null, callable $callable = null): void; 85 | 86 | /** 87 | * Toggles pets on or off from the database. 88 | */ 89 | public abstract function togglePets(string $owner, ?string $petName, callable $callable): void; 90 | 91 | /** 92 | * Updates the database with whether the pet is 93 | * chested or not. 94 | */ 95 | public abstract function updateChested(BasePet $pet): void; 96 | 97 | /** 98 | * Updates the database with the pet's inventory 99 | * contents. 100 | */ 101 | public abstract function updateInventory(BasePet $pet): void; 102 | 103 | /** 104 | * Called during plugin disable to let databases 105 | * close their instances. 106 | */ 107 | protected abstract function close(): void; 108 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/datastorage/MySQLDataStorer.php: -------------------------------------------------------------------------------- 1 | database->executeChange(SQLDataStorer::REGISTER_PET, [ 36 | "player" => $pet->getPetOwnerName(), 37 | "petname" => $pet->getPetName(), 38 | "entityname" => $pet->getEntityType(), 39 | "petsize" => $pet->getScale(), 40 | "isbaby" => (int) $pet->isBaby(), 41 | "chested" => (int) $pet->isChested(), 42 | "petlevel" => $pet->getPetLevel(), 43 | "levelpoints" => $pet->getPetLevelPoints() 44 | ]); 45 | } 46 | 47 | public function unregisterPet(BasePet $pet): void { 48 | $this->database->executeChange(SQLDataStorer::UNREGISTER_PET, [ 49 | "player" => $pet->getPetOwnerName(), 50 | "petname" => $pet->getPetName() 51 | ]); 52 | } 53 | 54 | public function load(string $player, callable $callable): void { 55 | $this->database->executeSelect(SQLDataStorer::LOAD_PLAYER_PETS, [ 56 | "player" => $player 57 | ], $callable); 58 | } 59 | 60 | public function getPlayerPets(string $player, ?string $entityName = null, callable $callable = null): void { 61 | $this->database->executeSelect(SQLDataStorer::LIST_PLAYER_PETS, [ 62 | "player" => $player, 63 | "entityname" => $entityName ?? "%" 64 | ], $callable); 65 | } 66 | 67 | public function getPetsLeaderboard(int $offset = 0, int $length = 1, ?string $entityName = null, callable $callable = null): void { 68 | $this->database->executeSelect(SQLDataStorer::PET_LEADERBOARD, [ 69 | "offset" => $offset, 70 | "length" => $length, 71 | "entityname" => $entityName ?? "%" 72 | ], $callable); 73 | } 74 | 75 | public function togglePets(string $ownerName, ?string $petName, callable $callable): void { 76 | $this->database->executeChange(SQLDataStorer::UPDATE_PET_VISIBILITY, [ 77 | "player" => $ownerName, 78 | "petname" => $petName ?? "%" 79 | ], function(int $changed) use ($ownerName, $petName, $callable): void { 80 | if($changed === 0) { 81 | $callable([]); 82 | } else { 83 | $this->database->executeSelect(SQLDataStorer::PET_VISIBILITY, [ 84 | "player" => $ownerName, 85 | "petname" => $petName ?? "%" 86 | ], $callable); 87 | } 88 | }); 89 | } 90 | 91 | public function updateExperience(BasePet $pet): void { 92 | $this->database->executeChange(SQLDataStorer::UPDATE_PET_EXPERIENCE, [ 93 | "petlevel" => $pet->getPetLevel(), 94 | "levelpoints" => $pet->getPetLevelPoints(), 95 | "player" => $pet->getPetOwnerName(), 96 | "petname" => $pet->getPetName() 97 | ]); 98 | } 99 | 100 | public function updateChested(BasePet $pet): void { 101 | $this->database->executeChange(SQLDataStorer::UPDATE_PET_CHESTED, [ 102 | "chested" => (int) $pet->isChested(), 103 | "player" => $pet->getPetOwnerName(), 104 | "petname" => $pet->getPetName() 105 | ]); 106 | } 107 | 108 | public function updateInventory(BasePet $pet): void { 109 | $this->database->executeChange(SQLDataStorer::UPDATE_PET_INVENTORY, [ 110 | "inventory" => $pet->getInventoryManager()->compressContents(), 111 | "player" => $pet->getPetOwnerName(), 112 | "petname" => $pet->getPetName() 113 | ]); 114 | } 115 | 116 | protected function prepare(): void { 117 | $loader = $this->getLoader(); 118 | 119 | $config = $loader->getBlockPetsConfig(); 120 | $this->type = strtolower($config->getDatabase()); 121 | $mc = $config->getMySQLInfo(); 122 | 123 | $libasynql_friendly_config = [ 124 | "type" => $this->type, 125 | "sqlite" => [ 126 | "file" => $loader->getDataFolder() . "pets.sqlite3" 127 | ], 128 | "mysql" => array_combine( 129 | ["host", "username", "password", "schema", "port"], 130 | [$mc["Host"], $mc["User"], $mc["Password"], $mc["Database"], $mc["Port"]] 131 | ) 132 | ]; 133 | 134 | $this->database = libasynql::create($loader, $libasynql_friendly_config, [ 135 | "mysql" => "database_stmts/mysql.sql", 136 | "sqlite" => "database_stmts/sqlite.sql" 137 | ]); 138 | 139 | $this->database->executeGeneric(SQLDataStorer::INITIALIZE_TABLES); 140 | 141 | $resource = $this->getLoader()->getResource("patches/" . $this->type . ".sql"); 142 | if($resource !== null) { 143 | $this->database->loadQueryFile($resource); //calls fclose($resource) 144 | } 145 | } 146 | 147 | public function patch(string $version): void { 148 | switch($version) { 149 | case "1.1.2": 150 | $this->database->executeGeneric(str_replace("{VERSION}", $version, SQLDataStorer::VERSION_PATCH)); 151 | break; 152 | } 153 | } 154 | 155 | protected function close(): void { 156 | $this->database->close(); 157 | } 158 | 159 | protected function reset(): void { 160 | $this->database->executeChange(SQLDataStorer::RESET); 161 | } 162 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/datastorage/SQLiteDataStorer.php: -------------------------------------------------------------------------------- 1 | database->executeSelect(SQLDataStorer::LOAD_PLAYER_PETS, [ 14 | "player" => $player 15 | ], function(array $rows) use($callable): void { 16 | foreach($rows as &$row) { 17 | if(isset($row["Inventory"])) { 18 | $row["Inventory"] = base64_decode($row["Inventory"]); 19 | } 20 | } 21 | $callable($rows); 22 | }); 23 | } 24 | 25 | public function updateInventory(BasePet $pet): void { 26 | $this->database->executeChange(SQLDataStorer::UPDATE_PET_INVENTORY, [ 27 | "inventory" => base64_encode($pet->getInventoryManager()->compressContents()), 28 | "player" => $pet->getPetOwnerName(), 29 | "petname" => $pet->getPetName() 30 | ]); 31 | } 32 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/pets/inventory/PetInventoryManager.php: -------------------------------------------------------------------------------- 1 | menu = InvMenu::create(InvMenu::TYPE_CHEST); 34 | $this->menu->setInventoryCloseListener(function(): void { 35 | $pet = $this->getPet(); 36 | $loader = $pet->getLoader(); 37 | if($loader->getBlockPetsConfig()->storeToDatabase()) { 38 | $loader->getDatabase()->updateInventory($pet); 39 | } 40 | }); 41 | $this->setName($pet->getPetName()); 42 | } 43 | 44 | public function setName(string $name): void { 45 | $this->menu->setName($name . "'s Inventory"); 46 | } 47 | 48 | public function getPet(): BasePet { 49 | return $this->pet; 50 | } 51 | 52 | public function getInventory(): Inventory { 53 | return $this->menu->getInventory(); 54 | } 55 | 56 | public function openAs(Player $player, ?int $forceId = null): void { 57 | $this->menu->send($player, $forceId); 58 | } 59 | 60 | public function load(string $compressed): void { 61 | $contents = []; 62 | 63 | /** @var CompoundTag $nbt */ 64 | foreach(self::$nbtSerializer->read($compressed)->mustGetCompoundTag()->getListTag("Inventory") as $nbt) { 65 | $contents[$nbt->getByte("Slot")] = Item::nbtDeserialize($nbt); 66 | } 67 | 68 | $this->getInventory()->setContents($contents); 69 | } 70 | 71 | public function compressContents(): string { 72 | $contents = []; 73 | 74 | foreach($this->getInventory()->getContents() as $slot => $item) { 75 | $contents[] = $item->nbtSerialize($slot); 76 | } 77 | 78 | $tag = CompoundTag::create()->setTag("Inventory", new ListTag($contents, NBT::TAG_Compound)); 79 | 80 | return self::$nbtSerializer->write(new TreeRoot($tag)); 81 | } 82 | } -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/tasks/BaseTask.php: -------------------------------------------------------------------------------- 1 | loader; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/BlockHorizons/BlockPets/tasks/PetRespawnTask.php: -------------------------------------------------------------------------------- 1 | pet->spawnToAll(); 17 | $this->pet->setDormant(false); 18 | } 19 | } --------------------------------------------------------------------------------