Repository: DRE2N/DungeonsXL Branch: master Commit: 89ab9cc3bd33 Files: 279 Total size: 1.1 MB Directory structure: gitextract_74xrxnj_/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── adapter/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── de/ │ └── erethon/ │ └── dungeonsxl/ │ └── adapter/ │ └── block/ │ └── BlockAdapter.java ├── addon/ │ ├── README.md │ ├── core/ │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── de/ │ │ │ └── erethon/ │ │ │ └── dungeonsxxl/ │ │ │ ├── DungeonsXXL.java │ │ │ ├── requirement/ │ │ │ │ └── FeeItemsRequirement.java │ │ │ ├── sign/ │ │ │ │ ├── FireworkSign.java │ │ │ │ ├── GlowingBlockSign.java │ │ │ │ ├── InteractWallSign.java │ │ │ │ └── ParticleSign.java │ │ │ ├── util/ │ │ │ │ ├── FireworkUtil.java │ │ │ │ └── GlowUtil.java │ │ │ └── world/ │ │ │ └── block/ │ │ │ └── GlowingBlock.java │ │ └── resources/ │ │ └── plugin.yml │ ├── dist/ │ │ └── pom.xml │ └── pom.xml ├── api/ │ ├── LICENSE │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── de/ │ └── erethon/ │ └── dungeonsxl/ │ └── api/ │ ├── DungeonModule.java │ ├── DungeonsAPI.java │ ├── Requirement.java │ ├── Reward.java │ ├── dungeon/ │ │ ├── BuildMode.java │ │ ├── CollectionGameRule.java │ │ ├── ConfigReader.java │ │ ├── Copier.java │ │ ├── Dungeon.java │ │ ├── Game.java │ │ ├── GameGoal.java │ │ ├── GameRule.java │ │ ├── GameRuleContainer.java │ │ └── MapGameRule.java │ ├── event/ │ │ ├── DataReloadEvent.java │ │ ├── group/ │ │ │ ├── GroupCollectRewardEvent.java │ │ │ ├── GroupCreateEvent.java │ │ │ ├── GroupDisbandEvent.java │ │ │ ├── GroupEvent.java │ │ │ ├── GroupFinishDungeonEvent.java │ │ │ ├── GroupFinishFloorEvent.java │ │ │ ├── GroupPlayerJoinEvent.java │ │ │ ├── GroupPlayerKickEvent.java │ │ │ ├── GroupPlayerLeaveEvent.java │ │ │ ├── GroupScoreEvent.java │ │ │ └── GroupStartFloorEvent.java │ │ ├── mob/ │ │ │ ├── DungeonMobDeathEvent.java │ │ │ ├── DungeonMobEvent.java │ │ │ └── DungeonMobSpawnEvent.java │ │ ├── player/ │ │ │ ├── EditPlayerEditEvent.java │ │ │ ├── EditPlayerEvent.java │ │ │ ├── EditPlayerLeaveEvent.java │ │ │ ├── GamePlayerDeathEvent.java │ │ │ ├── GamePlayerEvent.java │ │ │ ├── GamePlayerFinishEvent.java │ │ │ ├── GlobalPlayerEvent.java │ │ │ └── GlobalPlayerRewardPayOutEvent.java │ │ ├── requirement/ │ │ │ ├── RequirementCheckEvent.java │ │ │ ├── RequirementDemandEvent.java │ │ │ └── RequirementEvent.java │ │ ├── trigger/ │ │ │ ├── TriggerActionEvent.java │ │ │ ├── TriggerEvent.java │ │ │ ├── TriggerRegistrationEvent.java │ │ │ └── TriggerUnregistrationEvent.java │ │ └── world/ │ │ ├── EditWorldEvent.java │ │ ├── EditWorldGenerateEvent.java │ │ ├── EditWorldSaveEvent.java │ │ ├── EditWorldUnloadEvent.java │ │ ├── GameWorldEvent.java │ │ ├── GameWorldStartGameEvent.java │ │ ├── InstanceWorldEvent.java │ │ ├── InstanceWorldPostUnloadEvent.java │ │ ├── InstanceWorldUnloadEvent.java │ │ ├── ResourceWorldEvent.java │ │ └── ResourceWorldInstantiateEvent.java │ ├── mob/ │ │ ├── DungeonMob.java │ │ ├── ExternalMobProvider.java │ │ └── MobSet.java │ ├── player/ │ │ ├── EditPlayer.java │ │ ├── GamePlayer.java │ │ ├── GlobalPlayer.java │ │ ├── GroupAdapter.java │ │ ├── InstancePlayer.java │ │ ├── PlayerCache.java │ │ ├── PlayerClass.java │ │ └── PlayerGroup.java │ ├── sign/ │ │ ├── AbstractDSign.java │ │ ├── Button.java │ │ ├── Deactivatable.java │ │ ├── DungeonSign.java │ │ ├── Passive.java │ │ ├── Rocker.java │ │ └── Windup.java │ ├── trigger/ │ │ ├── AbstractTrigger.java │ │ ├── LogicalExpression.java │ │ ├── Trigger.java │ │ ├── TriggerListener.java │ │ └── TriggerTypeKey.java │ └── world/ │ ├── EditWorld.java │ ├── GameWorld.java │ ├── InstanceWorld.java │ └── ResourceWorld.java ├── build.bat ├── build.sh ├── bukkit_blockdata/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── de/ │ └── erethon/ │ └── dungeonsxl/ │ └── adapter/ │ └── block/ │ └── BlockAdapterBlockData.java ├── bukkit_magicvalues/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── de/ │ └── erethon/ │ └── dungeonsxl/ │ └── adapter/ │ └── block/ │ └── BlockAdapterMagicValues.java ├── core/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── de/ │ │ └── erethon/ │ │ └── dungeonsxl/ │ │ ├── DXLModule.java │ │ ├── DungeonsXL.java │ │ ├── command/ │ │ │ ├── BreakCommand.java │ │ │ ├── ChatCommand.java │ │ │ ├── ChatSpyCommand.java │ │ │ ├── CreateCommand.java │ │ │ ├── DCommand.java │ │ │ ├── DCommandRegistry.java │ │ │ ├── DeleteCommand.java │ │ │ ├── DungeonItemCommand.java │ │ │ ├── EditCommand.java │ │ │ ├── EnterCommand.java │ │ │ ├── EscapeCommand.java │ │ │ ├── GameCommand.java │ │ │ ├── GroupCommand.java │ │ │ ├── HelpCommand.java │ │ │ ├── ImportCommand.java │ │ │ ├── InviteCommand.java │ │ │ ├── KickCommand.java │ │ │ ├── LeaveCommand.java │ │ │ ├── ListCommand.java │ │ │ ├── LivesCommand.java │ │ │ ├── MainCommand.java │ │ │ ├── MsgCommand.java │ │ │ ├── PlayCommand.java │ │ │ ├── PortalCommand.java │ │ │ ├── ReloadCommand.java │ │ │ ├── RenameCommand.java │ │ │ ├── ResourcePackCommand.java │ │ │ ├── SaveCommand.java │ │ │ ├── StatusCommand.java │ │ │ ├── TestCommand.java │ │ │ └── UninviteCommand.java │ │ ├── config/ │ │ │ ├── DMessage.java │ │ │ └── MainConfig.java │ │ ├── dungeon/ │ │ │ ├── DDungeon.java │ │ │ ├── DGame.java │ │ │ └── DungeonConfig.java │ │ ├── global/ │ │ │ ├── DPortal.java │ │ │ ├── GameSign.java │ │ │ ├── GlobalProtection.java │ │ │ ├── GlobalProtectionCache.java │ │ │ ├── GlobalProtectionListener.java │ │ │ ├── GroupSign.java │ │ │ ├── JoinSign.java │ │ │ ├── LeaveSign.java │ │ │ └── UnloadedProtection.java │ │ ├── mob/ │ │ │ ├── CitizensMobProvider.java │ │ │ ├── CustomExternalMobProvider.java │ │ │ ├── DMob.java │ │ │ ├── DMobListener.java │ │ │ ├── DNPCRegistry.java │ │ │ └── ExternalMobPlugin.java │ │ ├── player/ │ │ │ ├── DEditPlayer.java │ │ │ ├── DGamePlayer.java │ │ │ ├── DGlobalPlayer.java │ │ │ ├── DGroup.java │ │ │ ├── DGroupTag.java │ │ │ ├── DInstancePlayer.java │ │ │ ├── DPermission.java │ │ │ ├── DPlayerData.java │ │ │ ├── DPlayerListener.java │ │ │ ├── RespawnTask.java │ │ │ ├── SecureModeTask.java │ │ │ ├── TimeIsRunningTask.java │ │ │ └── groupadapter/ │ │ │ └── PartiesAdapter.java │ │ ├── requirement/ │ │ │ ├── FeeLevelRequirement.java │ │ │ ├── FeeMoneyRequirement.java │ │ │ ├── FinishedDungeonsRequirement.java │ │ │ ├── ForbiddenItemsRequirement.java │ │ │ ├── GroupSizeRequirement.java │ │ │ ├── KeyItemsRequirement.java │ │ │ ├── PermissionRequirement.java │ │ │ ├── TimeSinceFinishRequirement.java │ │ │ ├── TimeSinceStartRequirement.java │ │ │ └── TimeframeRequirement.java │ │ ├── reward/ │ │ │ ├── ItemReward.java │ │ │ ├── LevelReward.java │ │ │ ├── MoneyReward.java │ │ │ └── RewardListener.java │ │ ├── sign/ │ │ │ ├── DSignListener.java │ │ │ ├── LocationSign.java │ │ │ ├── button/ │ │ │ │ ├── ActionBarSign.java │ │ │ │ ├── BossShopSign.java │ │ │ │ ├── ChatMessageSign.java │ │ │ │ ├── CheckpointSign.java │ │ │ │ ├── ClassesSign.java │ │ │ │ ├── EndSign.java │ │ │ │ ├── LeaveSign.java │ │ │ │ ├── LivesModifierSign.java │ │ │ │ ├── MessageSign.java │ │ │ │ ├── ReadySign.java │ │ │ │ ├── ResourcePackSign.java │ │ │ │ ├── SoundMessageSign.java │ │ │ │ ├── TeleportSign.java │ │ │ │ ├── TitleSign.java │ │ │ │ └── WaveSign.java │ │ │ ├── passive/ │ │ │ │ ├── BedSign.java │ │ │ │ ├── ChestSign.java │ │ │ │ ├── DungeonChestSign.java │ │ │ │ ├── FlagSign.java │ │ │ │ ├── HologramSign.java │ │ │ │ ├── InteractSign.java │ │ │ │ ├── LobbySign.java │ │ │ │ ├── NoteSign.java │ │ │ │ ├── PlaceSign.java │ │ │ │ ├── ProtectionSign.java │ │ │ │ ├── RewardChestSign.java │ │ │ │ ├── ScriptSign.java │ │ │ │ ├── SignScript.java │ │ │ │ └── StartSign.java │ │ │ ├── rocker/ │ │ │ │ ├── BlockSign.java │ │ │ │ ├── OpenDoorSign.java │ │ │ │ └── TriggerSign.java │ │ │ └── windup/ │ │ │ ├── CommandScript.java │ │ │ ├── CommandSign.java │ │ │ ├── CommandTask.java │ │ │ ├── DelayedPowerTask.java │ │ │ ├── DropSign.java │ │ │ ├── MobSign.java │ │ │ ├── MobSpawnTask.java │ │ │ └── RedstoneSign.java │ │ ├── trigger/ │ │ │ ├── DistanceTrigger.java │ │ │ ├── FortuneTrigger.java │ │ │ ├── InteractTrigger.java │ │ │ ├── MobTrigger.java │ │ │ ├── PresenceTrigger.java │ │ │ ├── ProgressTrigger.java │ │ │ ├── RedstoneTrigger.java │ │ │ ├── SignTrigger.java │ │ │ ├── TriggerListener.java │ │ │ ├── UseItemTrigger.java │ │ │ └── WaveTrigger.java │ │ ├── util/ │ │ │ ├── AttributeUtil.java │ │ │ ├── BlockUtilCompat.java │ │ │ ├── ContainerAdapter.java │ │ │ ├── DependencyVersion.java │ │ │ ├── LWCUtil.java │ │ │ ├── LocationString.java │ │ │ ├── ParsingUtil.java │ │ │ └── PlaceholderUtil.java │ │ └── world/ │ │ ├── DEditWorld.java │ │ ├── DGameWorld.java │ │ ├── DInstanceWorld.java │ │ ├── DResourceWorld.java │ │ ├── DWorldListener.java │ │ ├── LWCIntegration.java │ │ ├── SignData.java │ │ ├── WorldConfig.java │ │ └── block/ │ │ ├── GameBlock.java │ │ ├── LockedDoor.java │ │ ├── MultiBlock.java │ │ ├── PlaceableBlock.java │ │ ├── ProtectedBlock.java │ │ ├── RewardChest.java │ │ ├── TeamBed.java │ │ ├── TeamBlock.java │ │ └── TeamFlag.java │ └── resources/ │ ├── languages/ │ │ ├── english.yml │ │ ├── french.yml │ │ └── german.yml │ └── plugin.yml ├── dist/ │ └── pom.xml ├── mvnbt.jar └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a bug report to help us improve --- (Please follow this template, as doing so saves both you and me a lot of time. Issues that don't follow the template may be closed.) **Description** (A clear and concise description of what the bug is, e.g.: Every time my mob signs spawn pigs, they turn green and fly away.) **Reproduce** (List steps to reproduce, e.g.: 1. Create a new dungeon with /dxl create 2. Place [ready] sign, [mob]/pig/0,1/D5 sign 3. Leave edit mode with /dxl leave 4. Test with /dxl play 5. Trigger ready sign and get close to the location of the pig spawn sign) **Expected behavior** (A clear and concise description of what you expected to happen, e.g.: I wanted the pigs to turn blue and dig mole-like tunnels instead X( ) **Screenshots / GIFs / videos** (If applicable, add screenshots to help explain your problem.) **Relevant configuration files** (The three grave accents mark the beginning and end of a code block. If there are relevant configuration files, please paste them in the lines between the accents sothat others can see if there are any syntax errors.) ``` ``` ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project --- **Please describe the use case or the problem your request is related to.** (A clear and concise description of what the problem is. Ex. I'm always frustrated when [...];) **Describe the solution you'd like** (A clear and concise description of what you want to happen.) **Describe alternatives you've considered** (A clear and concise description of any alternative solutions or features you've considered.) ================================================ FILE: .github/ISSUE_TEMPLATE/question.md ================================================ --- name: Question about: About things you do not understand in the documentation --- **The feature you'd like to know more about** (e.g. game rules) **Link to the wiki article (in case it exists)** **What did you already try?** **Relevant configuration files** (The three grave accents mark the beginning and end of a code block. If there are relevant configuration files, please paste them in the lines between the accents sothat others can see if there are any syntax errors.) ``` ``` ================================================ FILE: .gitignore ================================================ *apache-maven-* licenseheader.txt *dependency-reduced-pom.xml *nb-configuration.xml *target ================================================ FILE: .travis.yml ================================================ language: java ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ ![DungeonsXL](https://erethon.de/resources/logos/DungeonsXL.png) [![Builds](https://erethon.de/resources/buttons/Builds.png)](http://erethon.de/repo/de/erethon/dungeonsxl/dungeonsxl-dist) [![Wiki](https://erethon.de/resources/buttons/Wiki.png)](../../wiki/) [![Issues](https://erethon.de/resources/buttons/Issues.png)](../../issues/) [![JavaDocs](https://erethon.de/resources/buttons/JavaDocs.png)](http://erethon.de/javadocs/dungeonsxl/) [![MCStats](https://erethon.de/resources/buttons/MCStats.png)](http://bstats.org/plugin/bukkit/DungeonsXL/) [![Build Status](https://travis-ci.com/DRE2N/DungeonsXL.svg?branch=master)](https://travis-ci.com/DRE2N/DungeonsXL) [![codebeat badge](https://codebeat.co/badges/5c57507f-084b-4945-8159-06bf5cf17794)](https://codebeat.co/projects/github-com-dre2n-dungeonsxl-master) DungeonsXL is a server mod that allows you to instantiate worlds. Its main goal is to offer a way to use a world in a set state multiple times by a player (like for a jump'n'run), a group of players (e.g. for a quest dungeon, an adventure map or a PvE arena) or even by groups of groups of players (e.g. for PvP arenas). DungeonsXL also provides custom game mechanics to make these worlds interesting. It might also be helpful if you want players to build something in creative mode quickly and uncomplicated without any influence on their main world data (inventory, levels etc.). ## Features * Create as many dungeons as you wish! * The instantiation system allows dungeons to be played by multiple groups of players at the same time without clashes. * Dungeons are accessable through portals in one of your main worlds. [Read more...](../../wiki/getting-started#entering-the-dungeon) * Invite players to edit single dungeons without the need to give them any further permissions. [Read more...](../../wiki/getting-started#editing-the-map) * Allow players to build in creative mode safely without any influence to their game progress in the main worlds! * Set checkpoints, breakable blocks, triggers, messages and much more through signs in the edit mode. [Read more...](../../wiki/signs) * Per dungeon configuration (you should try that after you became familiar with the basics of this plugin). [Read more...](../../wiki/dungeon-configuration) * Link multiple floors together to create large dungeons with multiple levels. [Read more...](../../wiki/getting-started#advanced-multi-floor-dungeons-mfds) * Use a dungeon as a tutorial and give them a PEX group when they finish it. [Read more...](../../wiki/main-configuration) * Players can play the dungeon with their own items or with configurable classes. * _The classes support doges!_ * Mob waves: [Read more...](../../wiki/signs#wave) * PvP * Time limits * A built-in custom mob system and support for MythicMobs. [Read more...](../../wiki/signs#mob) * A powerful API: [Read more...](../../wiki/api-tutorial) * Different game types allow you to use your maps dynamically for different purposes. [Read more...](../../wiki/game-types) * Announcements sothat users can join the next match easily. [Read more...](../../wiki/announcements) * Per dungeon resource packs * ...and many more! ## The concept If you want to learn how to use DungeonsXL step by step, please have a look at the [wiki](../../wiki) page [getting started](../../wiki/getting-started). ## Compatibility ### Server DungeonsXL works with Spigot 1.8.8 and higher. However, support for new versions has a higher priority than support for 1.8-1.12. Old builds that support older versions are unusable for production environments. See [here](../../wiki/legacy-support) for detailed information. DungeonsXL works with Spigot and Paper-based server softwares. This does not include Bukkit/Forge hybrids (MCPC+, Cauldron, Mohist, Magma, ...). ### XLib DungeonsXL requires [XLib](https://github.com/DRE2N/CaliburnAPI) to run. ### Building information and dependencies Building DungeonsXL from source requires [Java Development Kit 8 or higher](https://www.azul.com/downloads/?package=jdk#zulu). Both XLib and DXL can be built by running the build script in the respective root directory. Use _build.bat_ if you're on Windows and _build.sh_ on Linux, BSD or Mac. Dependencies are downloaded automatically. ### Known incompatibilities * Corpses * PerWorldInventory Many incompatibilities can be fixed with [PerWorldPlugins](http://dev.bukkit.org/bukkit-plugins/perworldplugins/) ([fork for 1.8+](https://www.spigotmc.org/resources/perworldplugins-unofficial-update-version.6454/)). Try to add the incompatible plugins only to the worlds where you need them. ================================================ FILE: adapter/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-adapter 0.19-SNAPSHOT jar de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT de.erethon.dungeonsxl dungeonsxl-api ${project.parent.version} compile org.spigotmc spigot-api ${spigotVersion.latest} provided ================================================ FILE: adapter/src/main/java/de/erethon/dungeonsxl/adapter/block/BlockAdapter.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.adapter.block; import de.erethon.dungeonsxl.api.player.PlayerGroup.Color; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; /** * @author Daniel Saukel */ public interface BlockAdapter { boolean isBedHead(Block block); void openDoor(Block block); void closeDoor(Block block); void setBlockWoolColor(Block block, Color color); BlockFace getFacing(Block block); void setFacing(Block block, BlockFace facing); void setAxis(Block block, boolean z); } ================================================ FILE: addon/README.md ================================================ ## DungeonsXL Donors Addon (C) 2020-2023 Daniel Saukel, All Rights Reserved. This module is a plugin with additional features made for donors. The GNU LGPLv3 of the API and the GNU GPLv3 license of the other modules do not apply to this module. ================================================ FILE: addon/core/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-addon-core ${project.parent.version} jar de.erethon.dungeonsxl dungeonsxl-addon 0.0.1-SNAPSHOT . true src/main/resources/ plugin.yml org.spigotmc spigot 1.16.5-R0.1-SNAPSHOT provided ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/DungeonsXXL.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonModule; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxxl.requirement.*; import de.erethon.dungeonsxxl.sign.*; import de.erethon.dungeonsxxl.util.GlowUtil; import de.erethon.xlib.compatibility.Internals; import de.erethon.xlib.plugin.PluginInit; import de.erethon.xlib.plugin.DREPluginSettings; import de.erethon.xlib.util.Registry; /** * @author Daniel Saukel */ public class DungeonsXXL extends PluginInit implements DungeonModule { private static DungeonsXXL instance; private DungeonsXL dxl; private GlowUtil glowUtil; public DungeonsXXL() { settings = DREPluginSettings.builder() .internals(Internals.v1_16_R3) .metrics(false) .spigotMCResourceId(-1) .build(); } @Override public void onEnable() { instance = this; dxl = DungeonsXL.getInstance(); glowUtil = new GlowUtil(this); } /** * Returns the instance of this plugin. * * @return the instance of this plugin */ public static DungeonsXXL getInstance() { return instance; } /** * Returns the current {@link de.erethon.dungeonsxl.DungeonsXL} singleton. * * @return the current {@link de.erethon.dungeonsxl.DungeonsXL} singleton */ public DungeonsXL getDXL() { return dxl; } /** * The loaded instance of GlowUtil. * * @return the loaded instance of GlowUtil */ public GlowUtil getGlowUtil() { return glowUtil; } @Override public void initRequirements(Registry> registry) { registry.add("feeItems", FeeItemsRequirement.class); } @Override public void initRewards(Registry> registry) { } @Override public void initSigns(Registry> registry) { registry.add("FIREWORK", FireworkSign.class); registry.add("GLOWINGBLOCK", GlowingBlockSign.class); registry.add("INTERACTWALL", InteractWallSign.class); registry.add("PARTICLE", ParticleSign.class); } @Override public void initGameRules(Registry registry) { } @Override public void initTriggers(Registry> triggerRegistry) { } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/requirement/FeeItemsRequirement.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import java.util.List; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class FeeItemsRequirement implements Requirement { private DungeonsAPI api; private List fee; public FeeItemsRequirement(DungeonsAPI api) { this.api = api; } public List getFee() { return fee; } @Override public void setup(ConfigurationSection config) { fee = api.getXLib().deserializeStackList(config, "feeItems"); } @Override public boolean check(Player player) { for (ItemStack stack : fee) { if (!player.getInventory().containsAtLeast(stack, stack.getAmount())) { return false; } } return true; } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_FEE_ITEMS + ": ").color(ChatColor.GOLD); boolean first = true; for (ItemStack stack : fee) { String name = stack.getAmount() > 1 ? stack.getAmount() + " " : "" + api.getXLib().getExItem(stack).getName(); ChatColor color = player.getInventory().containsAtLeast(stack, stack.getAmount()) ? ChatColor.GREEN : ChatColor.DARK_RED; if (!first) { builder.append(", ").color(ChatColor.WHITE); } else { first = false; } builder.append(name).color(color); } return builder.create(); } @Override public void demand(Player player) { player.getInventory().removeItem(fee.toArray(new ItemStack[]{})); } @Override public String toString() { return "FeeItemsRequirement{items=" + fee + "}"; } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/sign/FireworkSign.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxxl.util.FireworkUtil; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class FireworkSign extends Button { public FireworkSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Firework"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".firework"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { } @Override public void push() { FireworkUtil.spawnRandom(getSign().getLocation()); } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/sign/GlowingBlockSign.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Rocker; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxxl.DungeonsXXL; import de.erethon.dungeonsxxl.world.block.GlowingBlock; import de.erethon.xlib.util.EnumUtil; import org.bukkit.ChatColor; import org.bukkit.block.Sign; /** * Turns the attached block into a glowing block. * * @author Daniel Saukel */ public class GlowingBlockSign extends Rocker { private ChatColor color = ChatColor.DARK_RED; private Double time; private GlowingBlock glowingBlock; public GlowingBlockSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } /** * Returns the glowing block. * * @return the glowing block */ public GlowingBlock getGlowingBlock() { return glowingBlock; } /** * Returns the color of the glowing block or null if it is a rainbow block. * * @return the color of the glowing block or null if it is a rainbow block */ public ChatColor getColor() { return color; } @Override public String getName() { return "GlowingBlock"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".glowingblock"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { if (getLine(1).equalsIgnoreCase("RAINBOW")) { color = null; } else { ChatColor color = EnumUtil.getEnumIgnoreCase(ChatColor.class, getLine(1)); if (color != null) { this.color = color; } } try { time = Double.parseDouble(getLine(2)); } catch (NumberFormatException exception) { } } @Override public void activate() { if (active) { return; } ((DGameWorld) getGameWorld()).addGameBlock( glowingBlock = new GlowingBlock(DungeonsXXL.getInstance(), BlockUtilCompat.getAttachedBlock(getSign().getBlock()), color, time)); active = true; } @Override public void deactivate() { if (!active) { return; } glowingBlock.removeGlow(); active = false; } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/sign/InteractWallSign.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.sign.passive.InteractSign; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.util.BlockUtilCompat; import org.bukkit.block.Sign; /** * This sign adds an interact trigger to an attached block, like a "suspicious wall". * * @author Daniel Saukel */ public class InteractWallSign extends InteractSign { public InteractWallSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "InteractWall"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".interactwall"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return true; } @Override public void initialize() { String id = getSign().getLine(1); InteractTrigger trigger = (InteractTrigger) getGameWorld().createTrigger(this, LogicalExpression.parse("I" + id)); trigger.setInteractBlock(BlockUtilCompat.getAttachedBlock(getSign().getBlock())); } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/sign/ParticleSign.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.util.EnumUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Particle; import org.bukkit.block.Sign; /** * Spawns particles. * * @author Daniel Saukel */ public class ParticleSign extends Button { private Particle particle; private int count; private double offsetX, offsetY, offsetZ; private double extra = 1; public ParticleSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Particle"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".particle"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { particle = EnumUtil.getEnumIgnoreCase(Particle.class, getLine(1)); if (particle == null) { markAsErroneous("Unknown particle type: " + getLine(1)); return false; } return true; } @Override public void initialize() { String[] args = getLine(2).split(","); if (args.length == 1) { extra = NumberUtil.parseDouble(args[0], 1); } else if (args.length >= 3) { offsetX = NumberUtil.parseDouble(args[0], 0); offsetX = NumberUtil.parseDouble(args[1], 0); offsetX = NumberUtil.parseDouble(args[2], 0); if (args.length == 4) { extra = NumberUtil.parseDouble(args[3], 1); } } } @Override public void push() { getSign().getWorld().spawnParticle(particle, getSign().getLocation(), count, offsetX, offsetY, offsetZ, extra); } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/util/FireworkUtil.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.util; import java.util.Random; import org.bukkit.Color; import static org.bukkit.Color.*; import org.bukkit.FireworkEffect; import org.bukkit.Location; import org.bukkit.entity.EntityType; import org.bukkit.entity.Firework; import org.bukkit.inventory.meta.FireworkMeta; /** * Util class for randomized fireworks. * * @author Daniel Saukel */ public class FireworkUtil { private static final Random RANDOM = new Random(); private static final Color[] COLORS = {YELLOW, AQUA, BLACK, BLUE, FUCHSIA, GRAY, GREEN, LIME, MAROON, NAVY, OLIVE, ORANGE, PURPLE, RED, SILVER, TEAL, WHITE}; /** * Spawns a randomized firework. * * @param location the location where the firework is fired * @return the Firework */ public static Firework spawnRandom(Location location) { Firework firework = (Firework) location.getWorld().spawnEntity(location, EntityType.FIREWORK); FireworkMeta meta = firework.getFireworkMeta(); Random r = new Random(); int rt = r.nextInt(4) + 1; FireworkEffect.Type type = FireworkEffect.Type.BALL; if (rt == 1) { type = FireworkEffect.Type.BALL; } if (rt == 2) { type = FireworkEffect.Type.BALL_LARGE; } if (rt == 3) { type = FireworkEffect.Type.BURST; } if (rt == 4) { type = FireworkEffect.Type.CREEPER; } if (rt == 5) { type = FireworkEffect.Type.STAR; } FireworkEffect effect = FireworkEffect.builder().flicker(r.nextBoolean()).withColor(randomColor()).withFade(randomColor()).with(type).trail(r.nextBoolean()).build(); meta.addEffect(effect); int rp = r.nextInt(2) + 1; meta.setPower(rp); firework.setFireworkMeta(meta); return firework; } private static Color randomColor() { return COLORS[RANDOM.nextInt(COLORS.length - 1)]; } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/util/GlowUtil.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.util; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import net.minecraft.server.v1_16_R3.EntityShulker; import net.minecraft.server.v1_16_R3.EntityTypes; import net.minecraft.server.v1_16_R3.Packet; import net.minecraft.server.v1_16_R3.PacketPlayOutEntityDestroy; import net.minecraft.server.v1_16_R3.PacketPlayOutSpawnEntityLiving; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; import org.bukkit.craftbukkit.v1_16_R3.entity.CraftPlayer; import org.bukkit.entity.Player; import org.bukkit.entity.Shulker; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.plugin.Plugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scoreboard.Team; /** * @author Daniel Saukel */ public class GlowUtil implements Listener { private static final Random RANDOM = new Random(); private Map teams = new HashMap<>(); private GlowData glowingBlocks = new GlowData<>(); private Map> playerGlows = new HashMap<>(); private GlowRunnable runnable = new GlowRunnable(); public GlowUtil(Plugin plugin) { runnable.runTaskTimer(plugin, 0L, 2L); Bukkit.getPluginManager().registerEvents(this, plugin); } private Team getTeam(ChatColor color) { if (!teams.containsKey(color)) { Team team = Bukkit.getScoreboardManager().getMainScoreboard().getTeam("DXL_" + color.getChar()); if (team == null) { team = Bukkit.getScoreboardManager().getMainScoreboard().registerNewTeam("DXL_" + color.getChar()); team.setColor(color); } teams.put(color, team); } return teams.get(color); } /** * Adds a colored glow effect to the block that is visible to all players. * * @param block the block * @param color the glow color * @return the spawned entity that provides the glow effect */ public org.bukkit.entity.Entity addBlockGlow(Block block, ChatColor color) { Shulker entity = block.getWorld().spawn(new Location(block.getWorld(), block.getX() + .5, block.getY(), block.getZ() + .5), Shulker.class); entity.setAI(false); entity.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0)); entity.setInvulnerable(true); addGlow(entity, color); glowingBlocks.put(block, entity); return entity; } /** * Adds a packet-level colored glow effect to the block that is only visible to certain players. * * @param block the block * @param color the glow color * @param players the players who can see the effect */ public void addBlockGlow(Block block, ChatColor color, Player... players) { EntityShulker entity = new EntityShulker(EntityTypes.SHULKER, ((CraftWorld) block.getWorld()).getHandle()); entity.setLocation(block.getX() + .5, block.getY(), block.getZ() + .5, 0, 0); entity.setFlag(6, true); entity.setInvisible(true); for (Player player : players) { sendPacket(player, new PacketPlayOutSpawnEntityLiving(entity)); if (playerGlows.get(player) == null) { playerGlows.put(player, new GlowData<>()); } playerGlows.get(player).put(block, entity); } } /** * Adds a rainbow colored glow effect to the block that is visible to all players. * * @param block the block * @return the spawned entity that provides the glow effect */ public org.bukkit.entity.Entity addRainbowBlockGlow(Block block) { return addRainbowBlockGlow(block, (Long) null); } /** * Adds a rainbow colored glow effect to the block that is visible to all players. *

* The task is cancelled automatically when the entity dies. * * @param block the block * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever * @return the spawned entity that provides the glow effect */ public org.bukkit.entity.Entity addRainbowBlockGlow(Block block, Long cancelTime) { Shulker entity = block.getWorld().spawn(new Location(block.getWorld(), block.getX() + .5, block.getY(), block.getZ() + .5), Shulker.class); entity.setAI(false); entity.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0)); entity.setInvulnerable(true); glowingBlocks.put(block, entity); addRainbowGlow(entity, cancelTime); return entity; } /** * Adds a packet-level rainbow colored glow effect to the block that is only visible to certain players. *

* Returns the repeating task that handles color changes. * * @param block the block * @param players */ public void addRainbowBlockGlow(Block block, Player... players) { addRainbowBlockGlow(block, null, players); } /** * Adds a packet-level rainbow colored glow effect to the block that is only visible to certain players. *

* Returns the repeating task that handles color changes. * * @param block the block * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever * @param players */ public void addRainbowBlockGlow(Block block, Long cancelTime, Player... players) { EntityShulker entity = new EntityShulker(EntityTypes.SHULKER, ((CraftWorld) block.getWorld()).getHandle()); entity.setLocation(block.getX() + .5, block.getY(), block.getZ() + .5, 0, 0); entity.setFlag(6, true); entity.setInvisible(true); for (Player player : players) { sendPacket(player, new PacketPlayOutSpawnEntityLiving(entity)); if (playerGlows.get(player) == null) { playerGlows.put(player, new GlowData<>()); } playerGlows.get(player).put(block, entity); } addRainbowGlow(entity, cancelTime); } /** * Removes the glow effect from a glowing block. * * @param block the block */ public void removeBlockGlow(Block block) { org.bukkit.entity.Entity bukkitEntity = glowingBlocks.get(block); if (bukkitEntity != null) { bukkitEntity.remove(); glowingBlocks.remove(block); runnable.removeEntity(bukkitEntity); } for (Entry> entry : playerGlows.entrySet()) { net.minecraft.server.v1_16_R3.Entity nmsEntity = entry.getValue().get(block); if (nmsEntity != null) { sendPacket(entry.getKey(), new PacketPlayOutEntityDestroy(nmsEntity.getId())); runnable.removeEntity(nmsEntity); } } } /** * Adds a colored glow effect to an entity and handles its scoreboard team membership. * * @param entity a Bukkit Entity * @param color the glow color */ public void addGlow(org.bukkit.entity.Entity entity, ChatColor color) { getTeam(color).addEntry(asEntry(entity)); entity.setGlowing(true); } /** * Adds a colored glow effect to an entity and handles its scoreboard team membership. * * @param entity an NMS Entity * @param color the glow color */ public void addGlow(net.minecraft.server.v1_16_R3.Entity entity, ChatColor color) { getTeam(color).addEntry(asEntry(entity)); entity.setFlag(6, true); } /** * Adds a changing glow effect to an entity. * * @param entity an NMS Entity */ public void addRainbowGlow(org.bukkit.entity.Entity entity) { addRainbowGlow(entity, null); } /** * Adds a changing glow effect to an entity. * * @param entity an NMS Entity * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever */ public void addRainbowGlow(org.bukkit.entity.Entity entity, Long cancelTime) { entity.setGlowing(true); runnable.addEntity(entity, cancelTime != null ? System.currentTimeMillis() + cancelTime : null); } /** * Adds a changing glow effect to an entity. * * @param entity an NMS Entity */ public void addRainbowGlow(net.minecraft.server.v1_16_R3.Entity entity) { addRainbowGlow(entity, null); } /** * Adds a changing glow effect to an entity. * * @param entity an NMS Entity * @param cancelTime the time in milliseconds until the glow effect shall end; null = forever */ public void addRainbowGlow(net.minecraft.server.v1_16_R3.Entity entity, Long cancelTime) { entity.setFlag(6, true); runnable.addEntity(entity, cancelTime != null ? System.currentTimeMillis() + cancelTime : null); } /** * Removes the glow effect from an entity and handles its scoreboard team membership. * * @param entity a Bukkit Entity */ public void removeGlow(org.bukkit.entity.Entity entity) { entity.setGlowing(false); teams.values().forEach(t -> t.removeEntry(asEntry(entity))); runnable.removeEntity(entity); } /** * Removes the glow effect from an entity and handles its scoreboard team membership. * * @param entity an NMS Entity */ public void removeGlow(net.minecraft.server.v1_16_R3.Entity entity) { entity.setFlag(6, false); teams.values().forEach(t -> t.removeEntry(asEntry(entity))); runnable.removeEntity(entity); } private static String asEntry(org.bukkit.entity.Entity entity) { return entity instanceof Player ? entity.getName() : entity.getUniqueId().toString(); } private static String asEntry(net.minecraft.server.v1_16_R3.Entity entity) { return entity instanceof Player ? entity.getName() : entity.getUniqueID().toString(); } private static void sendPacket(Player player, Packet packet) { ((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet); } @EventHandler public void onBlockBreak(BlockBreakEvent event) { removeBlockGlow(event.getBlock()); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { playerGlows.remove(event.getPlayer()); } private class GlowRunnable extends BukkitRunnable { private Map entities = new HashMap<>(); private ChatColor color; private void addEntity(Object entity, Long cancelTime) { entities.put(entity, cancelTime); } private void removeEntity(Object entity) { entities.remove(entity); } @Override public void run() { color = ChatColor.values()[RANDOM.nextInt(ChatColor.values().length - 1)]; for (Entry entry : entities.entrySet().toArray(new Entry[entities.size()])) { if (entry.getKey() instanceof org.bukkit.entity.Entity) { run((org.bukkit.entity.Entity) entry.getKey(), entry.getValue()); } else if (entry.getKey() instanceof net.minecraft.server.v1_16_R3.Entity) { run((net.minecraft.server.v1_16_R3.Entity) entry.getKey(), entry.getValue()); } } } private void run(org.bukkit.entity.Entity entity, Long cancelTime) { getTeam(color).removeEntry(asEntry(entity)); if ((cancelTime != null && System.currentTimeMillis() >= cancelTime) || entity.isDead()) { entities.remove(entity); glowingBlocks.remove(entity); if (!entity.isDead()) { entity.setGlowing(false); } else { entity.remove(); } return; } getTeam(color).addEntry(asEntry(entity)); } private void run(net.minecraft.server.v1_16_R3.Entity entity, Long cancelTime) { getTeam(color).removeEntry(asEntry(entity)); if (cancelTime != null && System.currentTimeMillis() >= cancelTime) { entities.remove(entity); for (Entry> entry : playerGlows.entrySet()) { if (!entry.getValue().glowingBlocks.containsValue(entity)) { continue; } Player player = entry.getKey(); sendPacket(player, new PacketPlayOutEntityDestroy(entity.getId())); entry.getValue().remove(entity); } return; } getTeam(color).addEntry(asEntry(entity)); } } static class GlowData { Map glowingBlocks = new HashMap<>(); T get(Block block) { return glowingBlocks.get(block); } void remove(Block block) { glowingBlocks.remove(block); } void remove(T entity) { for (Entry entry : glowingBlocks.entrySet().toArray(new Entry[glowingBlocks.size()])) { if (entry.getValue().equals(entity)) { glowingBlocks.remove(entry.getKey()); } } } void put(Block block, T entity) { glowingBlocks.put(block, entity); } } } ================================================ FILE: addon/core/src/main/java/de/erethon/dungeonsxxl/world/block/GlowingBlock.java ================================================ /* * Copyright (C) 2020-2026 Daniel Saukel * * All rights reserved. */ package de.erethon.dungeonsxxl.world.block; import de.erethon.dungeonsxl.world.block.GameBlock; import de.erethon.dungeonsxxl.DungeonsXXL; import de.erethon.dungeonsxxl.util.GlowUtil; import org.bukkit.ChatColor; import org.bukkit.block.Block; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class GlowingBlock extends GameBlock { private GlowUtil glowUtil; public GlowingBlock(DungeonsXXL plugin, Block block, ChatColor color, Double time) { super(plugin.getDXL(), block); glowUtil = plugin.getGlowUtil(); Long millis; if (time != null) { millis = (long) (time * 1000); } else { millis = null; } if (color != null) { glowUtil.addBlockGlow(block, color); if (millis != null) { new BukkitRunnable() { @Override public void run() { removeGlow(); } }.runTaskLater(plugin, millis / 50); } } else { glowUtil.addRainbowBlockGlow(block, millis); } } public void removeGlow() { glowUtil.removeBlockGlow(block); } @Override public boolean onBreak(BlockBreakEvent event) { return false; } } ================================================ FILE: addon/core/src/main/resources/plugin.yml ================================================ name: DungeonsXXL main: de.erethon.dungeonsxxl.DungeonsXXL version: ${project.version}${buildNo} author: Daniel Saukel description: ${project.description} website: ${project.url} depend: [DungeonsXL] ================================================ FILE: addon/dist/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-addon-dist ${project.parent.version} jar de.erethon.dungeonsxl dungeonsxl-addon 0.0.1-SNAPSHOT ${project.artifactId}-${project.version}${buildNo} org.apache.maven.plugins maven-shade-plugin 3.6.1 package shade de.erethon.dungeonsxl dungeonsxl-addon-core ${project.parent.version} ================================================ FILE: addon/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-addon 0.0.1-SNAPSHOT pom DungeonsXXL https://dre2n.github.io Create BETTER custom dungeons and adventure maps with ease! de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT core dist de.erethon.dungeonsxl dungeonsxl-dist 0.19-SNAPSHOT provided ================================================ FILE: api/LICENSE ================================================ GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. ================================================ FILE: api/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-api 0.19-SNAPSHOT jar de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT org.apache.maven.plugins maven-javadoc-plugin 3.12.0 all,-missing attach-javadocs install javadoc jar org.spigotmc spigot-api ${spigotVersion.latest} provided ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/DungeonModule.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.xlib.util.Registry; /** * Class that manages initialization of several registries. *

* Addons should implement this interface and add their feature implementations to the registry in the respective method. * * @author Daniel Saukel */ public interface DungeonModule { /** * Initializes the {@link de.erethon.dungeonsxl.api.Requirement requirement} registry. * * @param requirementRegistry the registry */ void initRequirements(Registry> requirementRegistry); /** * Initializes the {@link de.erethon.dungeonsxl.api.Reward reward} registry. * * @param rewardRegistry the registry */ void initRewards(Registry> rewardRegistry); /** * Initializes the {@link de.erethon.dungeonsxl.api.sign.DungeonSign dungeon sign} registry. * * @param signRegistry the registry */ void initSigns(Registry> signRegistry); /** * Initializes the {@link de.erethon.dungeonsxl.api.dungeon.GameRule game rule} registry. * * @param gameRuleRegistry the registry */ void initGameRules(Registry gameRuleRegistry); /** * Initializes the {@link de.erethon.dungeonsxl.api.trigger.Trigger trigger} registry. * * @param triggerRegistry the registry */ void initTriggers(Registry> triggerRegistry); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/DungeonsAPI.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.mob.DungeonMob; import de.erethon.dungeonsxl.api.mob.ExternalMobProvider; import de.erethon.dungeonsxl.api.player.GroupAdapter; import de.erethon.dungeonsxl.api.player.PlayerCache; import de.erethon.dungeonsxl.api.player.PlayerClass; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.xlib.XLib; import de.erethon.xlib.mob.ExMob; import de.erethon.xlib.util.Registry; import java.io.File; import java.util.Collection; import org.bukkit.World; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.Plugin; /** * The API main interface. * * @author Daniel Saukel */ public interface DungeonsAPI extends Plugin { static final File PLUGIN_ROOT = new File("plugins/DungeonsXL"); static final File BACKUPS = new File(PLUGIN_ROOT, "backups"); static final File LANGUAGES = new File(PLUGIN_ROOT, "languages"); static final File MAPS = new File(PLUGIN_ROOT, "maps"); static final File PLAYERS = new File(PLUGIN_ROOT, "players"); static final File SCRIPTS = new File(PLUGIN_ROOT, "scripts"); static final File CLASSES = new File(SCRIPTS, "classes"); static final File DUNGEONS = new File(SCRIPTS, "dungeons"); /** * Returns the loaded instance of XLib. * * @return the loaded instance of XLib */ XLib getXLib(); /** * Returns a cache of player wrapper objects. * * @return a cache of player wrapper objects */ PlayerCache getPlayerCache(); /** * Returns a cache of Game objects. * * @return a cache of Game objects */ Collection getGameCache(); /** * Returns a registry of the loaded classes. * * @return a registry of the loaded classes */ Registry getClassRegistry(); /** * Returns a registry of the sign types. * * @return a registry of the sign types */ Registry> getSignRegistry(); /** * Returns a registry of the requirement types. * * @return a registry of the requirement types */ Registry> getRequirementRegistry(); /** * Returns a registry of the reward types. * * @return a registry of the reward types */ Registry> getRewardRegistry(); /** * Returns a registry of the dungeons. * * @return a registry of the dungeons */ Registry getDungeonRegistry(); /** * Returns a registry of the resources worlds. * * @return a registry of the resources worlds */ Registry getMapRegistry(); /** * Returns a cache of the instance worlds. * * @return a cache of the instance worlds */ Registry getInstanceCache(); /** * Returns a registry of the game rules. * * @return a registry of the game rules */ Registry getGameRuleRegistry(); /** * Returns a registry of the triggers. * * @return a registry of the triggers */ Registry> getTriggerRegistry(); /** * Returns a registry of the external mob providers. * * @return a registry of the external mob providers */ Registry getExternalMobProviderRegistry(); /** * Returns a cache of the player groups. * * @return a cache of the player groups */ Registry getGroupCache(); /** * Registers a DungeonModule. * * @param module the module to register */ void registerModule(DungeonModule module); /** * Makes DungeonsXL track external group and synchronize them with its own groups. * * @param groupAdapter the group adapter to register */ void registerGroupAdapter(GroupAdapter groupAdapter); /* Object initialization */ /** * Creates a new group. * * @param leader the leader * @return a new group */ PlayerGroup createGroup(Player leader); /** * Creates a new group. * * @param leader the leader * @param color the color that represents the group and sets the name * @return a new group or null if values are invalid */ PlayerGroup createGroup(Player leader, PlayerGroup.Color color); /** * Creates a new group. * * @param leader the leader * @param name the group's name - must be unique * @return a new group or null if values are invalid */ PlayerGroup createGroup(Player leader, String name); /** * Creates a new group. * * @param leader the leader * @param dungeon the dungeon to play * @return a new group or null if values are invalid */ PlayerGroup createGroup(Player leader, Dungeon dungeon); /** * Creates a new group. * * @param leader the leader * @param members the group members with or without the leader * @param name the name of the group * @param dungeon the dungeon to play * @return a new group or null if values are invalid */ PlayerGroup createGroup(Player leader, Collection members, String name, Dungeon dungeon); /** * Wraps the given {@link LivingEntity} object in a {@link DungeonMob} object. * * @param entity the entity * @param gameWorld the game world where the entity is * @param triggerId the identifier used in mob triggers * @return the wrapped DungeonMob */ DungeonMob wrapEntity(LivingEntity entity, GameWorld gameWorld, String triggerId); /** * Wraps the given {@link LivingEntity} object in a {@link DungeonMob} object. * * @param entity the entity * @param gameWorld the game world where the entity is * @param type the ExMob type of the entity * @return the wrapped DungeonMob */ DungeonMob wrapEntity(LivingEntity entity, GameWorld gameWorld, ExMob type); /** * Wraps the given {@link LivingEntity} object in a {@link DungeonMob} object. * * @param entity the entity * @param gameWorld the game world where the entity is * @param type the ExMob type of the entity * @param triggerId the identifier used in mob triggers * @return the wrapped DungeonMob */ DungeonMob wrapEntity(LivingEntity entity, GameWorld gameWorld, ExMob type, String triggerId); /* Getters */ /** * Returns an existing {@link DungeonMob} object that wraps the given {@link LivingEntity} object or null if none exists. * * @param entity the entity * @return an existing {@link DungeonMob} object that wraps the given {@link LivingEntity} object or null if none exists */ DungeonMob getDungeonMob(LivingEntity entity); /** * Returns the group the player is a member of or null if he is in none. * * @param member the player * @return the group the player is a member of or null if he is in none */ PlayerGroup getPlayerGroup(Player member); /** * Returns the game the given player plays. * * @param player the player * @return the game the given player plays */ Game getGame(Player player); /** * Returns the game played in the given instance world. * * @param world the instance world * @return the game played in the given instance world */ Game getGame(World world); /** * Returns the GameWorld that wraps the given instance world. * * @param world the instance world * @return the GameWorld that wraps the given instance world */ GameWorld getGameWorld(World world); /** * Returns the EditWorld that wraps the given instance world. * * @param world the instance world * @return the EditWorld that wraps the given instance worl */ EditWorld getEditWorld(World world); /** * Returns if the given world is an instance. * * @param world the world * @return if the given world is an instance */ boolean isInstance(World world); /** * Returns if the given item stack is a dungeon item. *

* Dungeon items are items that are removed from the inventory when the dungeon is finished. * * @param itemStack the item stack * @return if the given item stack is a dungeon item */ boolean isDungeonItem(ItemStack itemStack); /** * Sets the given item stack to be a dungeon item and returns a copy with the updated state. *

* Dungeon items are items that are removed from the inventory when the dungeon is finished. * * @param itemStack the item stack * @param dungeonItem if the item stack * @return a copy of the item stack that is a dungeon item */ ItemStack setDungeonItem(ItemStack itemStack, boolean dungeonItem); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/Requirement.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * Something a player needs to fulfill in order to be allowed to start the game (= trigger a ready sign). * * @author Daniel Saukel */ public interface Requirement { /** * Sets up the requirement from the given requirements {@link de.erethon.dungeonsxl.api.dungeon.GameRule} section. * * @param config the requirements config section */ void setup(ConfigurationSection config); /** * Returns if the given player fulfills the requirements. If true, this lets him start the game (= trigger a ready sign). * * @param player the player * @return if the given player fulfills the requirements */ boolean check(Player player); /** * Returns the message that informs the player if they fulfill the requirement. * * @param player the player who will receive the message * @return the error message that is sent to the player when they do not fulfill the requirement */ BaseComponent[] getCheckMessage(Player player); /** * This is fired after the {@link #check(Player)} has been accepted. It demands the requirement from the given player. This may be empty for a "key" or may * take something away for a "fee" requirement. * * @param player the player */ void demand(Player player); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/Reward.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api; import org.bukkit.entity.Player; /** * Something players are given when they successfully finish a {@link de.erethon.dungeonsxl.api.dungeon.Dungeon}. * * @see de.erethon.dungeonsxl.api.player.PlayerGroup#getRewards() * @author Daniel Saukel */ public interface Reward { /** * Gives the reward to the given player. * * @param player the player */ void giveTo(Player player); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/BuildMode.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import de.erethon.dungeonsxl.api.world.GameWorld; import java.util.HashMap; import java.util.Map; import org.bukkit.block.Block; import org.bukkit.entity.Player; /** * Checks for whether a block may be broken. * * @author Daniel Saukel */ public interface BuildMode { /** * Stores the pre-set breaking rules. */ static class Registry { /** * Entry keys must be lowercase. */ public static final Map ENTRIES = new HashMap<>(); static { ENTRIES.put("true", TRUE); ENTRIES.put("false", FALSE); ENTRIES.put("placed", PLACED); } } /** * All blocks except for protected ones may be broken. */ static final BuildMode TRUE = (Player player, GameWorld gameWorld, Block block) -> true; /** * Blocks may not be broken. */ static final BuildMode FALSE = (Player player, GameWorld gameWorld, Block block) -> false; /** * Blocks placed by players may be broken. */ static final BuildMode PLACED = (Player player, GameWorld gameWorld, Block block) -> gameWorld.getPlacedBlocks().contains(block); /** * Returns if the block can be broken or placed by the player. *

* The plugin protects dungeon signs before checking this. * * @param player the player who breaks or places the block * @param gameWorld the world the block is in * @param block the block * @return if the block can be broken or placed by the player */ boolean check(Player player, GameWorld gameWorld, Block block); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/CollectionGameRule.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import com.google.common.base.Verify; import de.erethon.dungeonsxl.api.DungeonsAPI; import java.util.Collection; import org.bukkit.configuration.ConfigurationSection; /** * A {@link GameRule} where the value is a {@link java.util.Collection}. * * @param the type of the collection * @param the type of the game rule value * @author Daniel Saukel */ public class CollectionGameRule> extends GameRule { protected Copier copier; /** * @param key the configuration key of the game rule * @param defaultValue the default value that is used when nothing is set; not null * @param copier a method to copy the collection */ public CollectionGameRule(String key, V defaultValue, Copier copier) { super(null, key, defaultValue); Verify.verifyNotNull(defaultValue, "defaultValue must not be null"); this.copier = copier; } /** * @param key the configuration key of the game rule * @param defaultValue the default value that is used when nothing is set * @param reader a functional interface that loads the value from config * @param copier a method to copy the collection */ public CollectionGameRule(String key, V defaultValue, ConfigReader reader, Copier copier) { super(null, key, defaultValue, reader); this.copier = copier; } /** * This implementation uses more expensive casting + catching the ClassCastException. * Developers should consider doing that themselves instead of wasting this cast. * * @param value the value * @return if the given value is an instance of {@link V} */ @Override public boolean isValidValue(Object value) { try { V v = (V) value; return true; } catch (ClassCastException exception) { return false; } } @Override public V fromConfig(DungeonsAPI api, GameRuleContainer container, ConfigurationSection config) { Object value = config.get(getKey()); if (reader != null) { V v = reader.read(api, value); setStateWithoutNull(container, v); return v; } V v; try { v = (V) value; } catch (ClassCastException exception) { return null; } setStateWithoutNull(container, v); return v; } private void setStateWithoutNull(GameRuleContainer container, V v) { while (v != null && v.contains(null)) { v.remove(null); } container.setState(this, v); } @Override public void merge(GameRuleContainer overriding, GameRuleContainer subsidiary, GameRuleContainer writeTo) { V writeToState = writeTo.getState(this); V write = writeToState != null ? copier.copy(writeTo.getState(this)) : null; if (subsidiary != writeTo) { V subsidiaryState = subsidiary.getState(this); if (subsidiaryState != null) { if (write == null) { write = copier.copy(subsidiaryState); } else { write.addAll(subsidiaryState); } } } if (overriding != writeTo) { V overridingState = overriding.getState(this); if (overridingState != null) { if (write == null) { write = copier.copy(overridingState); } else { write.addAll(overridingState); } } } writeTo.setState(this, write); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/ConfigReader.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.mob.ExMob; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * A functional interface to deserialize a raw value read from a configuration. * * @param the type of the object to read * @author Daniel Saukel */ @FunctionalInterface public interface ConfigReader { /** * Reads a set of XLib items. */ static final ConfigReader> EX_ITEM_SET_READER = (api, value) -> { if (!(value instanceof Collection)) { return null; } Set set = new HashSet<>(); for (Object entry : (Collection) value) { set.add(api.getXLib().getExItem(entry)); } return set; }; /** * Reads a set of XLib mobs. */ static final ConfigReader> EX_MOB_SET_READER = (api, value) -> { if (!(value instanceof Collection)) { return null; } Set set = new HashSet<>(); for (Object entry : (Collection) value) { set.add(api.getXLib().getExMob(entry)); } return set; }; /** * Reads a map of XLib items as tool keys and a set of XLib items as block values. */ static final ConfigReader>> TOOL_BLOCK_MAP_READER = (api, value) -> { if (!(value instanceof ConfigurationSection)) { return null; } ConfigurationSection section = (ConfigurationSection) value; Map> map = new HashMap<>(); for (Map.Entry entry : section.getValues(false).entrySet()) { ExItem tool = api.getXLib().getExItem(entry.getKey()); if (tool == null) { continue; } HashSet blocks = new HashSet<>(); blocks.addAll(api.getXLib().deserializeExItemList(section, entry.getKey())); map.put(tool, blocks); } return map; }; static final ConfigReader BUILD_MODE_READER = (api, value) -> { if (value instanceof Boolean) { return (Boolean) value ? BuildMode.TRUE : BuildMode.FALSE; } else if (value instanceof String) { return BuildMode.Registry.ENTRIES.get(((String) value).toLowerCase()); } else if (value instanceof List) { return (Player p, GameWorld w, Block b) -> ((List) value).contains(VanillaItem.get(b.getType()).getId()); } else { Map> whitelist = TOOL_BLOCK_MAP_READER.read(api, value); if (whitelist == null) { return null; } return (Player p, GameWorld w, Block b) -> { ExItem type = VanillaItem.get(b.getType()); ExItem breakTool = api.getXLib().getExItem(p.getItemInHand()); return whitelist.containsKey(type) && (whitelist.get(type) == null || whitelist.get(type).isEmpty() || whitelist.get(type).contains(breakTool)); }; } }; /** * Reads a game rule state from the configuration. * * @param api the DungeonsAPI instance * @param value the configuration object. This is the object received from using {@link org.bukkit.configuration.ConfigurationSection#get(String)} * with the String being {@link GameRule#getKey()}. * @return the game rule state read from configuration */ V read(DungeonsAPI api, Object value); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/Copier.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; /** * Copies an object. * * @param the type of the object to copy * @author Daniel Saukel */ @FunctionalInterface public interface Copier { /** * Returns a copy of the original. * * @param original the original * @return a copy of the original */ T copy(T original); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/Dungeon.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import de.erethon.dungeonsxl.api.world.ResourceWorld; import java.util.List; /** * A dungeon consists of floors and settings including its game rules. *

* MFD = multiple floor dungeon; SFD = single floor dungeon. * * @author Daniel Saukel */ public interface Dungeon { /** * Returns the name. * * @return the name */ String getName(); /** * Sets the name to the given value. * * @param name the name */ void setName(String name); /** * Returns if this dungeon has multiple floors. * * @return if this dungeon has multiple floors */ boolean isMultiFloor(); /** * Returns the map to instantiate. *

* This method is the same as {@link #getStartFloor()} but a bit more intuitive for SFDs. * * @return the map to instantiate */ default ResourceWorld getMap() { return getStartFloor(); } /** * Returns the first floor of this dungeon. * * @return the first floor of this dungeon */ ResourceWorld getStartFloor(); /** * Sets the first floor of this dungeon. * * @param startFloor the startFloor to set */ void setStartFloor(ResourceWorld startFloor); /** * Returns the last floor of this dungeon or null if this is an SFD. * * @return the last floor of this dungeon or null if this is an SFD */ ResourceWorld getEndFloor(); /** * Sets the last floor of this MFD. * * @param endFloor the last floor */ void setEndFloor(ResourceWorld endFloor); /** * Returns a list of the floors without start and end floor. * * @return a list of the floors without start and end floor */ List getFloors(); /** * Adds the given floor. * * @param resource the resource to add */ void addFloor(ResourceWorld resource); /** * Removes the given floor. * * @param resource the resource to remove */ void removeFloor(ResourceWorld resource); /** * Returns the amount of floors in this dungeon including start and end floor. *

* This may be less than the size of {@link #getFloors()} + 2 if not all floors from the list are used. * * @return the amount of floors in this dungeon including start and end floor */ int getFloorCount(); /** * Sets the amount of floors that shall be played. * * @param floorCount the amount of floors to set */ void setFloorCount(int floorCount); /** * Returns if floors cannot be played once if floors are selected randomly from the list. * * @return the removeWhenPlayed if floors cannot be played once if floors are selected randomly from the list */ boolean getRemoveWhenPlayed(); /** * Sets if floors cannot be played once if floors are selected randomly from the list. * * @param removeWhenPlayed if floors cannot be played once if floors are selected randomly from the list */ void setRemoveWhenPlayed(boolean removeWhenPlayed); /** * The values from this game rule container will override all values of the game rule containers of the dungeon's maps. * * @return the override values */ GameRuleContainer getOverrideValues(); /** * The values from this game rule container will be overriden by values of the game rule containers of the dungeon's maps. They will however still override * the values from the main config. * * @return the default values */ GameRuleContainer getDefaultValues(); /** * Returns true if the floor is either in the floors list or the start / end floor. * * @param resource the ResourceWorld to check * @return true if the floor is either in the floors list or the start / end floor. */ default boolean containsFloor(ResourceWorld resource) { if (isMultiFloor()) { return getFloors().contains(resource) || getStartFloor().equals(resource) || getEndFloor().equals(resource); } else { return getMap().equals(resource); } } /** * Returns true if the floor is either in the floors list or the start / end floor. * * @param mapName the name of the map to check * @return true if the floor is either in the floors list or the start / end floor. */ default boolean containsFloor(String mapName) { for (ResourceWorld world : getFloors()) { if (world.getName().equals(mapName)) { return true; } } return getStartFloor().getName().equals(mapName) || getEndFloor().getName().equals(mapName); } /** * Returns the rules of this game. *

* This is not necessarily represented 1:1 by a config file because it is usually merged together through {@link #setupRules()}. * * @return the rules of this game */ GameRuleContainer getRules(); /** * Sets the rules of the game. * * @param rules the rules */ void setRules(GameRuleContainer rules); /** * Sets up the rules with the following priority: 1. Game type 2. Dungeon config: Override values 3. Floor config 4. Dungeon config: Default values 5. Main * config: Default values 6. The default values */ void setupRules(); /** * Returns false if there are errors in the setup; true if not. * * @return false if there are errors in the setup; true if not */ boolean isSetupCorrect(); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/Game.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import java.util.Collection; import java.util.List; import org.bukkit.entity.Player; /** * Handles the rules of playing in a dungeon. *

* Tracks the progress of groups in a dungeon and handles their interaction with each other. * * @author Daniel Saukel */ // Implementation-specific methods: wave and kill counter methods public interface Game { /** * Returns if this is a tutorial game. * * @return if this is a tutorial game */ boolean isTutorial(); /** * Sets if this is a tutorial game * * @param tutorial if this is a tutorial game */ void setTutorial(boolean tutorial); /** * Returns a read-only List of the groups that are playing this game. * * @return a read-only List of the groups that are playing this game */ List getGroups(); /** * Adds the given group to this game. * * @param group the group */ void addGroup(PlayerGroup group); /** * Removes the given group from this game. * * @param group the group */ void removeGroup(PlayerGroup group); /** * Returns if the group has started (=if the ready sign has been triggered). * * @return if the group has started */ boolean hasStarted(); /** * Sets the status of the game to have started / not yet started. * * @param started if the game has started */ void setStarted(boolean started); /** * Returns the game instance in which this game takes place. * * @return the game instance in which this game takes place */ GameWorld getWorld(); /** * Sets the game instance in which this game takes place. * * @param gameWorld the game instance in which this game takes place */ void setWorld(GameWorld gameWorld); /** * Returns if the game has rewards. * * @return if the game has rewards */ boolean hasRewards(); /** * Sets if the game has rewards. * * @param enabled if the game has rewards */ void setRewards(boolean enabled); /** * Returns the rules of the dungeon of this game. *

* This is not necessarily represented 1:1 by a config file because it is usually merged together through {@link Dungeon#setupRules()}. * * @return the rules of the dungeon of this game */ default GameRuleContainer getRules() { return getDungeon().getRules(); } /** * Returns a read-only List of the remaining floors to play. * * @return a read-only List of the remaining floors to play */ List getUnplayedFloors(); /** * Adds a floor to the list of floors to play. * * @param unplayedFloor the resource world of the floor * @return if the addition was successful */ boolean addUnplayedFloor(ResourceWorld unplayedFloor); /** * Removes a floor from the list of floors to play. * * @param unplayedFloor the resource world of the floor * @param force if the floor shall be removed even if the {@link #getDungeon() dungeon}'s floors are not to be * {@link Dungeon#getRemoveWhenPlayed() removed when played.} * @return if the removal was successful */ boolean removeUnplayedFloor(ResourceWorld unplayedFloor, boolean force); /** * Returns the resource of the next floor to play. * * @return the resource of the next floor to play */ ResourceWorld getNextFloor(); /** * Sets the next floor to play. * * @param floor the resource world of the floor */ void setNextFloor(ResourceWorld floor); /** * Returns the amount of played floors in this game. * * @return the amount of played floors in this game */ int getFloorCount(); /** * Returns the dungeon that "hosts" this game. * * @return the dungeon that "hosts" this game */ Dungeon getDungeon(); /** * Returns the players playing the game. * * @return the players playing the game */ Collection getPlayers(); /** * Returns true if there are no groups in this game; false if not. * * @return true if there are no groups in this game; false if not */ boolean isEmpty(); /** * Returns and, if necessary, instantiates the game world. * * @param ignoreLimit if the instance limit set in the main config shall be ignored * @return the game world */ GameWorld ensureWorldIsLoaded(boolean ignoreLimit); /** * Starts the game. This is what happens when the ready sign is triggered by everyone. * * @return if the game has started correctly */ boolean start(); /** * Deletes this game. */ void delete(); /** * Returns true if all groups of the game have finished it; false if not. * * @return true if all groups of the game have finished it; false if not */ default boolean isFinished() { return getGroups().stream().allMatch(PlayerGroup::isFinished); } /** * Sends a message to each player in each group. * * @param message the message. Supports color codes */ default void sendMessage(String message) { getGroups().forEach(g -> g.sendMessage(message)); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameGoal.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import de.erethon.xlib.util.EnumUtil; import org.bukkit.configuration.ConfigurationSection; /** * A game goal defines what the players have to do in order to finish the game. * * @author Daniel Saukel */ public class GameGoal extends GameRuleContainer { /** * Score used for capture the flag and similar game types. */ public static final GameRule INITIAL_SCORE = new GameRule<>(Integer.class, "initialScore", 3); /** * The amount of goals to score before the game ends. -1 = not used. */ public static final GameRule SCORE_GOAL = new GameRule<>(Integer.class, "scoreGoal", -1); /** * The time left to finish the game; -1 if no timer is used. */ public static final GameRule TIME_TO_FINISH = new GameRule<>(Integer.class, "timeToFinish", -1); /** * The default game goal: {@link Type#END} without TIME_TO_FINISH */ public static final GameGoal DEFAULT = new GameGoal(Type.END); static { DEFAULT.setState(TIME_TO_FINISH, TIME_TO_FINISH.getDefaultValue()); } /** * The reader to deserialize a game goal from a configuration. */ public static final ConfigReader READER = (api, value) -> { if (!(value instanceof ConfigurationSection)) { return DEFAULT; } ConfigurationSection config = (ConfigurationSection) value; Type type = EnumUtil.getEnumIgnoreCase(Type.class, config.getString("type", "END")); GameGoal goal = new GameGoal(type); for (GameRule rule : type.getComponents()) { rule.fromConfig(api, goal, config); if (!goal.rules.containsKey(rule)) { goal.setState(rule, rule.getDefaultValue()); } } return goal; }; private Type type; public GameGoal(Type type) { this.type = type; } /** * Returns the type of the game goal. * * @return the type */ public Type getType() { return type; } /** * Determines the behavior of the game goal and which settings apply to it. */ public enum Type { /** * The default goal. The game ends when the end is reached. */ END(TIME_TO_FINISH), /** * The game ends when a player dies and only one group is left. */ LAST_MAN_STANDING, /** * SCORE_GOAL = -1: The game does not end. Instead, the goal is to survive as long as possible to beat a highscore. *

* SCORE_GOAL > 0: The game ends when a group reachs a specific score. *

* TIME_TO_FINISH != -1: The game ends after a specific time. The goal is to get the highest score until then. */ SCORE(INITIAL_SCORE, SCORE_GOAL, TIME_TO_FINISH), /** * The game ends after a specific time. The goal is to survive until then. */ TIME_SURVIVAL(TIME_TO_FINISH); private GameRule[] components; Type(GameRule... components) { this.components = components; } /** * Returns an array of the game rules that apply to game goals of this type. * * @return an array of the game rules that apply to game goals of this type */ public GameRule[] getComponents() { return components; } /** * Returns whether the given game rule applies to game goals of this type. * * @param component the game rule * @return whether the given game rule applies to game goals of this type */ public boolean hasComponent(GameRule component) { for (GameRule c : components) { if (c == component) { return true; } } return false; } } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameRule.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.Reward; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.mob.ExMob; import de.erethon.xlib.util.EnumUtil; import de.erethon.xlib.util.NumberUtil; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bukkit.Difficulty; import org.bukkit.GameMode; import org.bukkit.configuration.ConfigurationSection; /** * Represents a game rule for a {@link Game}. * * @param the type of the game rule value * @author Daniel Saukel */ public class GameRule { /** * Shall players play the dungeon with their own items or do you want to use classes? */ public static final GameRule KEEP_INVENTORY_ON_ENTER = new GameRule<>(Boolean.class, "keepInventoryOnEnter", false); /** * Shall players keep their inventory when they leave the dungeon without succeeding? */ public static final GameRule KEEP_INVENTORY_ON_ESCAPE = new GameRule<>(Boolean.class, "keepInventoryOnEscape", false); /** * Shall players keep their inventory when they finish the dungeon? */ public static final GameRule KEEP_INVENTORY_ON_FINISH = new GameRule<>(Boolean.class, "keepInventoryOnFinish", false); /** * Shall players lose their items when they die (do not mix up this with "onEscape"!)? */ public static final GameRule KEEP_INVENTORY_ON_DEATH = new GameRule<>(Boolean.class, "keepInventoryOnDeath", true); /** * Shall players reset their inventory to their chosen class when respawning? */ public static final GameRule RESET_CLASS_INVENTORY_ON_RESPAWN = new GameRule<>(Boolean.class, "resetClassInventoryOnRespawn", false); /** * The location where the players spawn when they leave the dungeon without succeeding. */ public static final GameRule ESCAPE_LOCATION = new GameRule<>(String.class, "escapeLocation", null); /** * The location where the players spawn when they finish the dungeon. */ public static final GameRule FINISH_LOCATION = new GameRule<>(String.class, "finishLocation", null); /** * The goal of the game that defines what makes it end. */ public static final GameRule GAME_GOAL = new GameRule<>(GameGoal.class, "gameGoal", GameGoal.DEFAULT, GameGoal.READER); /** * The Vanilla game mode. */ public static final GameRule GAME_MODE = new GameRule<>(GameMode.class, "gameMode", GameMode.SURVIVAL); /** * The Vanilla difficulty. */ public static final GameRule DIFFICULTY = new GameRule<>(Difficulty.class, "difficulty", Difficulty.NORMAL); /** * If the food levels of the players change. */ public static final GameRule FOOD_LEVEL = new GameRule<>(Boolean.class, "foodLevel", true); /** * Sets if death screens are enabled. If false, players that would have died are healed and teleported to the respawn location; * their inventory and experience are dropped if {@link #KEEP_INVENTORY_ON_DEATH} is set to false. */ public static final GameRule DEATH_SCREEN = new GameRule<>(Boolean.class, "deathScreen", false); /** * If players may fly. */ public static final GameRule FLY = new GameRule<>(Boolean.class, "fly", false); /** * If players can build and destroy blocks in this world. */ public static final GameRule BREAK_BLOCKS = new GameRule<>(BuildMode.class, "breakBlocks", BuildMode.FALSE, ConfigReader.BUILD_MODE_READER); /** * A blacklist of block types players cannot interact with. */ public static final GameRule>> INTERACTION_BLACKLIST = new MapGameRule<>("interactionBlacklist", new HashMap<>(), ConfigReader.TOOL_BLOCK_MAP_READER, HashMap::new); /** * A list of all entity types that shall be protected from damage. */ public static final GameRule> DAMAGE_PROTECTED_ENTITIES = new CollectionGameRule<>("damageProtectedEntities", new HashSet<>(), ConfigReader.EX_MOB_SET_READER, HashSet::new); /** * A list of all entity types that shall be protected from interaction. */ public static final GameRule> INTERACTION_PROTECTED_ENTITIES = new CollectionGameRule<>("interactionProtectedEntities", new HashSet<>(), ConfigReader.EX_MOB_SET_READER, HashSet::new); /** * If blocks may be placed. */ public static final GameRule PLACE_BLOCKS = new GameRule<>(BuildMode.class, "placeBlocks", BuildMode.FALSE, ConfigReader.BUILD_MODE_READER); /** * A set of blocks that do not fade. * * @see org.bukkit.event.block.BlockFadeEvent */ public static final GameRule> BLOCK_FADE_DISABLED = new CollectionGameRule<>("blockFadeDisabled", new HashSet<>(), ConfigReader.EX_ITEM_SET_READER, HashSet::new); /** * This does what the doFireTick Vanilla game rule does. */ public static final GameRule FIRE_TICK = new GameRule<>(Boolean.class, "fireTick", false); /** * If it should rain permanently in the dungeon. *

* true = permanent rain; false = permanent sun; leaving this out = random weather like in vanilla Minecraft */ public static final GameRule RAIN = new GameRule<>(Boolean.class, "rain", null); /** * Thunderstorms. * * @see #RAIN */ public static final GameRule THUNDER = new GameRule<>(Boolean.class, "thunder", null); /** * The time ticks (to be used like in the vanilla /time command). */ public static final GameRule TIME = new GameRule<>(Long.class, "time", null); /** * PvP */ public static final GameRule PLAYER_VERSUS_PLAYER = new GameRule<>(Boolean.class, "playerVersusPlayer", false); /** * Friendly fire refers just to members of the same group. */ public static final GameRule FRIENDLY_FIRE = new GameRule<>(Boolean.class, "friendlyFire", false); /** * Amount of lives a player initially has when he enters a dungeon. */ public static final GameRule INITIAL_LIVES = new GameRule<>(Integer.class, "initialLives", -1); /** * Alternatively to {@link #INITIAL_LIVES player lives}, you can use group lives. */ public static final GameRule INITIAL_GROUP_LIVES = new GameRule<>(Integer.class, "initialGroupLives", -1); /** * When loot may be taken away out of the dungeon again. */ public static final GameRule TIME_TO_NEXT_LOOT = new GameRule<>(Integer.class, "timeToNextLoot", 0); /** * The cooldown between two mob waves. */ public static final GameRule TIME_TO_NEXT_WAVE = new GameRule<>(Integer.class, "timeToNextWave", 10); /** * Time until a player is kicked out of a group after he leaves the server. */ public static final GameRule TIME_UNTIL_KICK_OFFLINE_PLAYER = new GameRule<>(Integer.class, "timeUntilKickOfflinePlayer", 0); /** * A list of requirements. Note that requirements will be ignored if the player has the dxl.ignorerequirements permission node. */ public static final GameRule> REQUIREMENTS = new CollectionGameRule<>("requirements", new ArrayList<>(), (api, value) -> { if (!(value instanceof ConfigurationSection)) { return null; } ConfigurationSection section = (ConfigurationSection) value; List requirements = new ArrayList<>(); for (String key : section.getValues(false).keySet()) { Class clss = api.getRequirementRegistry().get(key); if (clss == null) { MessageUtil.log(api, "&4Could not find requirement named \"" + key + "\"."); continue; } try { Constructor constructor = clss.getConstructor(DungeonsAPI.class); if (constructor == null) { MessageUtil.log(api, "&4Requirement \"" + key + "\" is not implemented properly with a (DungeonsAPI) constructor."); continue; } Requirement requirement = (Requirement) constructor.newInstance(api); requirement.setup(section); requirements.add(requirement); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { MessageUtil.log(api, "&4Requirement \"" + key + "\" is not implemented properly with a (DungeonsAPI) constructor."); } } return requirements; }, ArrayList::new); /** * This can be used to give rewards. The default implementation does not do this at the moment. */ public static final GameRule> REWARDS = new CollectionGameRule<>("rewards", new ArrayList<>(), (api, value) -> { if (!(value instanceof ConfigurationSection)) { return null; } ConfigurationSection section = (ConfigurationSection) value; List rewards = new ArrayList<>(); for (String key : section.getValues(false).keySet()) { Class clss = api.getRewardRegistry().get(key); if (clss == null) { MessageUtil.log(api, "&4Could not find reward named \"" + key + "\"."); continue; } try { Constructor constructor = clss.getConstructor(DungeonsAPI.class); if (constructor == null) { MessageUtil.log(api, "&4Reward \"" + key + "\" is not implemented properly with a (DungeonsAPI) constructor."); continue; } Reward reward = (Reward) constructor.newInstance(api); // reward.setup(); rewards.add(reward); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { MessageUtil.log(api, "&4Reward \"" + key + "\" is not implemented properly with a (DungeonsAPI) constructor."); } } return rewards; }, ArrayList::new); /** * These commands can be used by all players if they are in the dungeon. DXL commands like /dxl leavecan be used by default. */ public static final GameRule> GAME_COMMAND_WHITELIST = new CollectionGameRule<>("gameCommandWhitelist", new ArrayList<>(), ArrayList::new); /** * A list of permissions players get while they play the game. The permissions get removed as soon as the player leaves the game. Requires Vault and a * permissions plugin like PermissionsEx. */ public static final GameRule> GAME_PERMISSIONS = new CollectionGameRule<>("gamePermissions", new ArrayList<>(), ArrayList::new); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule TITLE = new GameRule<>(String.class, "title.title", null); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule SUBTITLE = new GameRule<>(String.class, "title.subtitle", null); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule ACTION_BAR = new GameRule<>(String.class, "title.actionBar", null); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule CHAT = new GameRule<>(String.class, "title.chat", null); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule TITLE_FADE_IN = new GameRule<>(Integer.class, "title.fadeIn", 20); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule TITLE_FADE_OUT = new GameRule<>(Integer.class, "title.fadeOut", 20); /** * Use this to replace the default ready / new floor message. If titles are deactivated in the main config, this is not going to work. */ public static final GameRule TITLE_SHOW = new GameRule<>(Integer.class, "title.show", 60); /** * Messages; also to be created with /dxl msg */ public static final GameRule> MESSAGES = new MapGameRule<>("messages", new HashMap<>(), (api, value) -> { if (!(value instanceof ConfigurationSection)) { return null; } ConfigurationSection section = (ConfigurationSection) value; Map map = new HashMap<>(); for (Map.Entry entry : section.getValues(false).entrySet()) { int id = NumberUtil.parseInt(entry.getKey(), -1); if (id == -1) { continue; } if (!(entry.getValue() instanceof String)) { continue; } map.put(id, (String) entry.getValue()); } return map; }, HashMap::new); /** * Items you cannot drop or destroy. */ public static final GameRule> SECURE_OBJECTS = new CollectionGameRule<>("secureObjects", new HashSet<>(), ConfigReader.EX_ITEM_SET_READER, HashSet::new); /** * If group tags are used. */ public static final GameRule GROUP_TAG_ENABLED = new GameRule<>(Boolean.class, "groupTagEnabled", false); /** * If Citizens NPCs should be copied to the native registry. */ public static final GameRule USE_NATIVE_CITIZENS_REGISTRY = new GameRule<>(Boolean.class, "useNativeCitizensRegistry", false); /** * If mobs shall drop experience or a whitelist of mobs that drop experience, while all others do not. */ public static final GameRule MOB_EXP_DROPS = new GameRule(Object.class, "mobExpDrops", false, (api, value) -> value instanceof Boolean ? value : ConfigReader.EX_MOB_SET_READER.read(api, value) ); /** * If mobs shall drop items or a whitelist of mobs that drop items, while all others do not. */ public static final GameRule MOB_ITEM_DROPS = new GameRule(Object.class, "mobItemDrops", false, (api, value) -> value instanceof Boolean ? value : ConfigReader.EX_MOB_SET_READER.read(api, value) ); /** * An array of all game rules that exist natively in DungeonsXL. */ public static final GameRule[] VALUES = values(); /** * A container of all rules with their default value. This is used internally as the most subsidiary container that fills missing rules if they are not set. */ public static final GameRuleContainer DEFAULT_VALUES = new GameRuleContainer(); static { for (GameRule rule : VALUES) { DEFAULT_VALUES.setState(rule, rule.getDefaultValue()); } } private static GameRule[] values() { Field[] fields = GameRule.class.getFields(); GameRule[] values = new GameRule[fields.length - 2]; int i = 0; for (Field field : fields) { try { Object object = field.get(null); if (object instanceof GameRule) { values[i++] = (GameRule) object; } } catch (IllegalArgumentException | IllegalAccessException exception) { exception.printStackTrace(); } } return values; } protected Class type; protected ConfigReader reader; private String key; private V defaultValue; /** * @param type the class of V * @param key the configuration key of the game rule * @param defaultValue the default value that is used when nothing is set */ public GameRule(Class type, String key, V defaultValue) { this.type = type; this.key = key; this.defaultValue = defaultValue; } /** * @param type the class of V * @param key the configuration key of the game rule * @param defaultValue the default value that is used when nothing is set * @param reader a functional interface that loads the value from config */ public GameRule(Class type, String key, V defaultValue, ConfigReader reader) { this(type, key, defaultValue); this.reader = reader; } /** * Returns the configuration key of the game rule. * * @return the configuration key of the game rule */ public String getKey() { return key; } /** * Returns the value used if nothing is specified by a game rule provider. * * @return the value used if nothing is specified by a game rule provider */ public V getDefaultValue() { return defaultValue; } /** * Returns if the given value is an instance of {@link V}. * * @param value the value * @return if the given value is an instance of {@link V} */ public boolean isValidValue(Object value) { return type.isInstance(value); } /** * Returns the state of the game rule fetched from the config. *

* If the type of this game rule is an enum, Strings as config values that are the {@link Enum#name()} of an enum value are converted automatically. * * @param api the API instance * @param container the game rule container whose state is to be set * @param config the config to fetch the value from * @return the value */ public V fromConfig(DungeonsAPI api, GameRuleContainer container, ConfigurationSection config) { Object value = config.get(getKey()); if (reader != null) { V v = reader.read(api, value); container.setState(this, v); return v; } if (Enum.class.isAssignableFrom(type)) { if (!(value instanceof String)) { return null; } value = EnumUtil.getEnumIgnoreCase((Class) type, (String) value); } if (isValidValue(value)) { container.setState(this, (V) value); return (V) value; } else { return null; } } /** * Compares the state attached to the game rule of two GameRuleContainers. *

* This may be overriden if necessary, for example if the value is a {@link java.util.Collection} and the desired behavior is to merge the values instead of * keeping the overriding one. * * @param overriding the state of this container will by default be copied to the "writeTo" container if it is not null * @param subsidiary the state of this container will by default be copied to the "writeTo" container if the state of the "overriding" container is null * @param writeTo the state of the game rule will be set to the one of either "overriding" or "subsidiary". This container may be == to one of the * others. */ public void merge(GameRuleContainer overriding, GameRuleContainer subsidiary, GameRuleContainer writeTo) { V overridingValue = overriding.getState(this); V subsidiaryValue = subsidiary.getState(this); writeTo.setState(this, overridingValue != null ? overridingValue : subsidiaryValue); } @Override public String toString() { return getClass().getSimpleName() + "{key=" + key + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/GameRuleContainer.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import java.util.HashMap; import java.util.Map; /** * A container for {@link GameRule}s. * * @author Daniel Saukel */ public class GameRuleContainer { protected Map, Object> rules; /** * Initializes an emtpy GameRuleContainer. */ public GameRuleContainer() { rules = new HashMap<>(); } /** * Copies a GameRuleContainer. * * @param container the container to copy */ public GameRuleContainer(GameRuleContainer container) { rules = new HashMap<>(container.rules); } /** * Returns the state of the GameRule or UNDEFINED_STATE if it is not defined * * @param the type of the value of the rule * @param rule the rule * @return the state of the rule */ public V getState(GameRule rule) { if (!rules.containsKey(rule)) { return null; } else { return (V) rules.get(rule); } } /** * Sets the state of the GameRule.Set it to null to remove the rule from the map sothat a subsidiary provider can set it. * * @param the type of the value of the rule * @param rule the rule * @param state the new state of the rule in this container */ public void setState(GameRule rule, V state) { if (state == null) { rules.remove(rule); } else if (rule.isValidValue(state)) { rules.put(rule, state); } else { throw new IllegalArgumentException("state is not a valid value for rule " + rule.getKey()); } } /** * Removes the rule from the map sothat a subsidiary container can set it. * * @param rule the GameRule to unset */ public void unsetState(GameRule rule) { rules.remove(rule); } /** * Fills the values that are not yet set with values from a subsidiary container. * * @param subsidiary the GameRules that override the values that are null. */ public void merge(GameRuleContainer subsidiary) { subsidiary.rules.entrySet().forEach(e -> e.getKey().merge(this, subsidiary, this)); } @Override public String toString() { return "GameRuleContainer{" + rules + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/dungeon/MapGameRule.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.dungeon; import com.google.common.base.Verify; import de.erethon.dungeonsxl.api.DungeonsAPI; import java.util.Map; import org.bukkit.configuration.ConfigurationSection; /** * A {@link GameRule} where the value is a {@link java.util.Map}. * * @param the type of the map key * @param the type of the map value * @param the type of the game rule value * @author Daniel Saukel */ public class MapGameRule> extends GameRule { protected Copier copier; /** * @param key the configuration key of the game rule * @param defaultValue the default value that is used when nothing is set; not null * @param reader a functional interface that loads the value from config * @param copier a method to copy the map */ public MapGameRule(String key, V defaultValue, ConfigReader reader, Copier copier) { super(null, key, defaultValue, reader); Verify.verifyNotNull(defaultValue, "defaultValue must not be null"); this.copier = copier; } /** * This implementation uses more expensive casting + catching the ClassCastException. * Developers should consider doing that themselves instead of wasting this cast. * * @param value the value * @return if the given value is an instance of {@link V} */ @Override public boolean isValidValue(Object value) { try { V v = (V) value; return true; } catch (ClassCastException exception) { return false; } } @Override public V fromConfig(DungeonsAPI api, GameRuleContainer container, ConfigurationSection config) { if (reader == null) { return null; } V v = reader.read(api, config.getConfigurationSection(getKey())); if (v == null) { return null; } v.remove(null); // Do not allow null values container.setState(this, v); return v; } @Override public void merge(GameRuleContainer overriding, GameRuleContainer subsidiary, GameRuleContainer writeTo) { V writeToState = writeTo.getState(this); V write = writeToState != null ? copier.copy(writeTo.getState(this)) : null; V subsidiaryState = subsidiary.getState(this); if (subsidiaryState != null) { if (write == null) { write = copier.copy(subsidiaryState); } else { write.putAll(subsidiaryState); } } V overridingState = overriding.getState(this); if (overridingState != null) { if (write == null) { write = copier.copy(overridingState); } else { write.putAll(overridingState); } } writeTo.setState(this, write); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/DataReloadEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; /** * Fired when the plugin is reloaded with /dxl reload. * * @author Daniel Saukel */ public class DataReloadEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName(); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupCollectRewardEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group collects a reward. *

* In the default implementation, this happens when a player opens a reward chest. * * @author Daniel Saukel */ public class GroupCollectRewardEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GamePlayer collector; private Reward reward; public GroupCollectRewardEvent(PlayerGroup group, GamePlayer collector, Reward reward) { super(group); this.collector = collector; this.reward = reward; } /** * Returns the player who collected the reward. *

* Note that this may be null if addons add a way to give rewards that cannot be attributed to one collector. * * @return the player who collected the reward */ public GamePlayer getCollector() { return collector; } /** * Returns the reward the group collected. * * @return the reward the group collected */ public Reward getReward() { return reward; } /** * Sets the reward the group collected. * * @param reward the reward */ public void setReward(Reward reward) { this.reward = reward; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; collector=" + collector + "; reward=" + reward + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupCreateEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group is created explicitly or implicitly. * * @author Daniel Saukel */ public class GroupCreateEvent extends GroupEvent implements Cancellable { /** * The reason why the group is created. */ public enum Cause { ANNOUNCER, COMMAND, /** * When a group is created to mirror the state of a party plugin. * * @see de.erethon.dungeonsxl.api.player.GroupAdapter */ GROUP_ADAPTER, GROUP_SIGN, TUTORIAL, /** * When a group is created by an addon. */ CUSTOM } private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GlobalPlayer creator; private Cause cause; public GroupCreateEvent(PlayerGroup group, GlobalPlayer creator, Cause cause) { super(group); this.creator = creator; this.cause = cause; } /** * Returns the player who created the group. * * @return the player who created the group */ public GlobalPlayer getCreator() { return creator; } /** * Returns the cause for the group creation. * * @return the cause for the group creation */ public Cause getCause() { return cause; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; creator=" + creator + "; cause=" + cause + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupDisbandEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group is disbanded. * * @author Daniel Saukel */ public class GroupDisbandEvent extends GroupEvent implements Cancellable { public enum Cause { COMMAND, DUNGEON_FINISHED, GROUP_ADAPTER, GROUP_IS_EMPTY, LOST, CUSTOM } private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GlobalPlayer disbander; private Cause cause; public GroupDisbandEvent(PlayerGroup group, Cause cause) { super(group); this.cause = cause; } public GroupDisbandEvent(PlayerGroup group, GlobalPlayer disbander, Cause cause) { super(group); this.disbander = disbander; this.cause = cause; } /** * The player who disbanded the group. *

* This is null if the cause is {@link Cause#DUNGEON_FINISHED}, {@link Cause#GROUP_ADAPTER}, {@link Cause#LOST} or {@link Cause#CUSTOM}. * * @return the player who disbanded the group */ public GlobalPlayer getDisbander() { return disbander; } /** * Returns the cause for the group deletion. * * @return the cause for the group deletion */ public Cause getCause() { return cause; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; disbander=" + disbander + "; cause=" + cause + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Event; /** * Superclass for events involving DungeonsXL groups. * * @author Daniel Saukel */ public abstract class GroupEvent extends Event { protected PlayerGroup group; protected GroupEvent(PlayerGroup group) { this.group = group; } /** * Returns the group involved in this event. * * @return the group involved in this event */ public PlayerGroup getGroup() { return group; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupFinishDungeonEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group finishs a {@link Dungeon}, which means the end floor of a dungeon. *

* Do not confuse this with {@link de.erethon.dungeonsxl.api.event.player.GamePlayerFinishEvent}. GamePlayerFinishEvent is fired when a player triggers an end * sign, while GroupFinishDungeonEvent is triggered when all group members have triggered the end sign and the game actually ends. * * @author Daniel Saukel */ public class GroupFinishDungeonEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private Dungeon dungeon; public GroupFinishDungeonEvent(PlayerGroup group, Dungeon dungeon) { super(group); this.dungeon = dungeon; } /** * Returns the dungeon the group was playing. * * @return the dungeon the group was playing */ public Dungeon getDungeon() { return dungeon; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; dungeon=" + dungeon + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupFinishFloorEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group finishs a dungeon floor. * * @author Daniel Saukel */ public class GroupFinishFloorEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GameWorld finished; private ResourceWorld next; public GroupFinishFloorEvent(PlayerGroup group, GameWorld finished, ResourceWorld next) { super(group); this.finished = finished; this.next = next; } /** * Returns the game world that was just finished. * * @return the game world that was just finished */ public GameWorld getFinished() { return finished; } /** * Returns the resource world of the next floor. * * @return the resource world of the next floor */ public ResourceWorld getNext() { return next; } /** * Sets the next floor to load. *

* If one has already been loaded because another group finished the floor earlier, this will not do anything. * * @param next the next floor to load */ public void setNext(ResourceWorld next) { this.next = next; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; finished=" + finished + "; next=" + next + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupPlayerJoinEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player joins a DungeonsXL group. * * @author Daniel Saukel */ public class GroupPlayerJoinEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GlobalPlayer player; private boolean creator; public GroupPlayerJoinEvent(PlayerGroup group, GlobalPlayer player, boolean creator) { super(group); this.player = player; this.creator = creator; } /** * Returns the player who is joining the group. * * @return the player who is joining the group */ public GlobalPlayer getPlayer() { return player; } /** * Returns if the player is the creator of the group. * * @return if the player is the creator of the group */ public boolean isCreator() { return creator; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; player=" + player + "; creator=" + creator + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupPlayerKickEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player is kicked out of a group. * * @author Daniel Saukel */ public class GroupPlayerKickEvent extends GroupEvent implements Cancellable { public enum Cause { COMMAND, /** * When the player is kicked because he does not have any lives left. */ DEATH, /** * When a player is kicked from a group to mirror the state of a party plugin. * * @see de.erethon.dungeonsxl.api.player.GroupAdapter */ GROUP_ADAPTER, OFFLINE, /** * When the time for the group to reach a certain state expired. */ TIME_EXPIRED, CUSTOM } private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GlobalPlayer player; private Cause cause; public GroupPlayerKickEvent(PlayerGroup group, GlobalPlayer player, Cause cause) { super(group); this.player = player; this.cause = cause; } /** * Returns the player who is joining the group. * * @return the player who is joining the group */ public GlobalPlayer getPlayer() { return player; } /** * Returns the cause of the kick. * * @return the cause of the kick */ public Cause getCause() { return cause; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; player=" + player + "; cause=" + cause + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupPlayerLeaveEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player leaves a group. * * @author Daniel Saukel */ public class GroupPlayerLeaveEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GlobalPlayer player; public GroupPlayerLeaveEvent(PlayerGroup group, GlobalPlayer player) { super(group); this.player = player; } /** * Returns the player who left the group. * * @return the player who left the group */ public GlobalPlayer getPlayer() { return player; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; player=" + player + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupScoreEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group scores a point. * * @author Daniel Saukel */ public class GroupScoreEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GamePlayer scorer; private PlayerGroup loserGroup; public GroupScoreEvent(PlayerGroup group, GamePlayer scorer, PlayerGroup loserGroup) { super(group); this.scorer = scorer; this.loserGroup = loserGroup; } /** * Returns the player who scored. * * @return the player who scored */ public GamePlayer getScorer() { return scorer; } /** * Returns the group that lost a score to the scorers. * * @return the group that lost a score to the scorers */ public PlayerGroup getLoserGroup() { return loserGroup; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; scorer=" + scorer + "; loserGroup=" + loserGroup + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/group/GroupStartFloorEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.group; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a group starts playing a floor. * * @author Daniel Saukel */ public class GroupStartFloorEvent extends GroupEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private GameWorld gameWorld; public GroupStartFloorEvent(PlayerGroup group, GameWorld gameWorld) { super(group); this.gameWorld = gameWorld; } /** * Returns the game instance. * * @return the game instance */ public GameWorld getGameWorld() { return gameWorld; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{group=" + group + "; gameWorld=" + gameWorld + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/mob/DungeonMobDeathEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.mob; import de.erethon.dungeonsxl.api.mob.DungeonMob; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a {@link DungeonMob} dies. * * @author Daniel Saukel */ public class DungeonMobDeathEvent extends DungeonMobEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; public DungeonMobDeathEvent(DungeonMob mob) { super(mob); } /** * Returns the player who killed the mob or null if the cause of its death was not a player. * * @return the player who killed the mob or null if the cause of its death was not a player */ public Player getKiller() { if (mob.getEntity() == null) { return null; } return mob.getEntity().getKiller(); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{mob=" + mob + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/mob/DungeonMobEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.mob; import de.erethon.dungeonsxl.api.mob.DungeonMob; import org.bukkit.entity.LivingEntity; import org.bukkit.event.Event; /** * Superclass for events involving DungeonsXL mobs. * * @author Daniel Saukel */ public abstract class DungeonMobEvent extends Event { protected DungeonMob mob; protected DungeonMobEvent(DungeonMob mob) { this.mob = mob; } /** * Returns the DungeonMob involved in this event. * * @return the DungeonMob involved in this event */ public DungeonMob getDungeonMob() { return mob; } /** * Returns the Bukkit LivingEntity involved in this event. * * @return the Bukkit LivingEntity involved in this event */ public LivingEntity getBukkitEntity() { return mob.getEntity(); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/mob/DungeonMobSpawnEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.mob; import de.erethon.dungeonsxl.api.mob.DungeonMob; import org.bukkit.event.HandlerList; /** * Fired when a spawned entity is registered as a {@link DungeonMob}. *

* Use {@link org.bukkit.event.entity.CreatureSpawnEvent} if you need to prevent a mob from spawning. * * @author Daniel Saukel */ public class DungeonMobSpawnEvent extends DungeonMobEvent { private static final HandlerList handlers = new HandlerList(); public DungeonMobSpawnEvent(DungeonMob mob) { super(mob); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public String toString() { return getClass().getSimpleName() + "{mob=" + mob + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/EditPlayerEditEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.EditPlayer; import org.bukkit.event.HandlerList; /** * Fired when a player starts editing a dungeon map. * * @author Daniel Saukel */ public class EditPlayerEditEvent extends EditPlayerEvent { private static final HandlerList handlers = new HandlerList(); private boolean newlyLoaded; public EditPlayerEditEvent(EditPlayer editPlayer, boolean newlyLoaded) { super(editPlayer); this.newlyLoaded = newlyLoaded; } /** * Returns true if the edit world was not instantiated before the player edited it and false if it was. * * @return true if the edit world was not instantiated before the player edited it and false if it was */ public boolean isNewlyLoaded() { return newlyLoaded; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public String toString() { return getClass().getSimpleName() + "{player=" + globalPlayer + "; newlyLoaded=" + newlyLoaded + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/EditPlayerEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.EditPlayer; /** * Superclass for events involving {@link EditPlayer}s. * * @author Daniel Saukel */ public abstract class EditPlayerEvent extends GlobalPlayerEvent { protected EditPlayerEvent(EditPlayer editPlayer) { super(editPlayer); } /** * Returns the EditPlayer involved in this event. * * @return the EditPlayer involved in this event */ public EditPlayer getEditPlayer() { return (EditPlayer) globalPlayer; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/EditPlayerLeaveEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.EditPlayer; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player stops editing a dungeon map. * * @author Daniel Saukel */ public class EditPlayerLeaveEvent extends EditPlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private boolean escape; private boolean unloadIfEmpty; public EditPlayerLeaveEvent(EditPlayer editPlayer, boolean escape, boolean unloadIfEmpty) { super(editPlayer); this.escape = escape; this.unloadIfEmpty = unloadIfEmpty; } /** * Returns false if the edit world is saved, true if not. * * @return false if the edit world is saved, true if not */ public boolean isEscape() { return escape; } /** * Returns if the instance shall be unloaded when it is empty after the player left. * * @return if the instance shall be unloaded when it is empty after the player left */ public boolean getUnloadIfEmpty() { return unloadIfEmpty; } /** * Sets if the instance shall be unloaded when it is empty after the player left. * * @param unloadIfEmpty if the instance shall be unloaded when it is empty after the player left */ public void setUnloadIfEmpty(boolean unloadIfEmpty) { this.unloadIfEmpty = unloadIfEmpty; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{player=" + globalPlayer + "; escape=" + escape + "; unloadIfEmpty=" + unloadIfEmpty + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/GamePlayerDeathEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.GamePlayer; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player dies in a dungeon. This is also fired when a player does not technically die because the deathScreen rule prevented the death. * * @author Daniel Saukel */ public class GamePlayerDeathEvent extends GamePlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private boolean keepInventory; private int lostLives; public GamePlayerDeathEvent(GamePlayer gamePlayer, boolean keepInventory, int lostLives) { super(gamePlayer); this.lostLives = lostLives; this.keepInventory = keepInventory; } /** * If the player's state - including his inventory, EXP etc. - is kept. * * @return if the player's state is kept */ public boolean isInventoryKept() { return keepInventory; } /** * Sets if the player's state - including his inventory, EXP etc. - is kept. * * @param keepInventory if the player's state is kept */ public void setInventoryKept(boolean keepInventory) { this.keepInventory = keepInventory; } /** * Returns the amount of lives the player loses. * * @return the amount of lives the player loses */ public int getLostLives() { return lostLives; } /** * Sets the amount of lives the player loses. * * @param lostLives the lives the player loses */ public void setLostLives(int lostLives) { this.lostLives = lostLives; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{player=" + globalPlayer + "; keepInventory=" + keepInventory + "; lostLives=" + lostLives + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/GamePlayerEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.GamePlayer; /** * Superclass for events involving {@link GamePlayer}s. * * @author Daniel Saukel */ public abstract class GamePlayerEvent extends GlobalPlayerEvent { protected GamePlayerEvent(GamePlayer gamePlayer) { super(gamePlayer); } /** * Returns the GamePlayer involved in this event. * * @return the GamePlayer involved in this event */ public GamePlayer getGamePlayer() { return (GamePlayer) globalPlayer; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/GamePlayerFinishEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.GamePlayer; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player finishs a game. *

* Do not confuse this with {@link de.erethon.dungeonsxl.api.event.group.GroupFinishDungeonEvent}. GamePlayerFinishEvent is fired when a player triggers an end * sign, while GroupFinishDungeonEvent is triggered when all group members have triggered the ready sign and the game actually ends. * * @author Daniel Saukel */ public class GamePlayerFinishEvent extends GamePlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private boolean hasToWait; public GamePlayerFinishEvent(GamePlayer gamePlayer, boolean hasToWait) { super(gamePlayer); this.hasToWait = hasToWait; } /** * Returns false if the other group members have all already triggered the end sign, true if not. * * @return false if the other group members have all already triggered the end sign, true if not */ public boolean getHasToWait() { return hasToWait; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{player=" + globalPlayer + "; hasToWait=" + hasToWait + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/GlobalPlayerEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import org.bukkit.entity.Player; import org.bukkit.event.Event; /** * Superclass for events involving DungeonsXL players. * * @author Daniel Saukel */ public abstract class GlobalPlayerEvent extends Event { protected GlobalPlayer globalPlayer; protected GlobalPlayerEvent(GlobalPlayer globalPlayer) { this.globalPlayer = globalPlayer; } /** * Returns the GlobalPlayer involved in this event * * @return the GlobalPlayer involved in this event */ public GlobalPlayer getGlobalPlayer() { return globalPlayer; } /** * Returns the Bukkit Player involved in this event * * @return the Bukkit Player involved in this event */ public Player getBukkitPlayer() { if (globalPlayer == null) { return null; } return globalPlayer.getPlayer(); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/player/GlobalPlayerRewardPayOutEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.player; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import java.util.List; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a player gets his {@link Reward}s after finishing a game. * * @see GlobalPlayer#setRewardItems(java.util.List) * @author Daniel Saukel */ public class GlobalPlayerRewardPayOutEvent extends GlobalPlayerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private final List rewards; public GlobalPlayerRewardPayOutEvent(GlobalPlayer globalPlayer, List rewards) { super(globalPlayer); this.rewards = rewards; } /** * Returns a list of the rewards the player will get. * * @return a list of the rewards the player will get */ public List getRewards() { return rewards; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{player=" + globalPlayer + "; rewards=" + rewards + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/requirement/RequirementCheckEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.requirement; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when it is checked if a player fulfills a {@link Requirement} realized through the Requirement API. *

* Note that this is usually called twice per player: When he tries to enter a dungeon and when he tries to start a game. * * @author Daniel Saukel */ public class RequirementCheckEvent extends RequirementEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private Player player; private boolean keepInventory; private BaseComponent[] checkMessage; public RequirementCheckEvent(Requirement requirement, Dungeon dungeon, Player player, boolean keepInventory) { super(requirement, dungeon); this.player = player; this.keepInventory = keepInventory; checkMessage = requirement.getCheckMessage(player); } /** * Returns the checked player. * * @return the player */ public Player getPlayer() { return player; } /** * Sets the checked player. * * @param player the player */ public void setPlayer(Player player) { this.player = player; } /** * Returns the message that will be sent to the player to inform him what he needs in order to fulfill the requirement if there is a requirement that he * does not fulfill. * * @return the message that will be sent to the player to inform him what he needs in order to fulfill the requirement if there is a requirement that he * does not fulfill */ public BaseComponent[] getCheckMessage() { return checkMessage; } /** * Sets the message that will be sent to the player to inform him what he needs in order to fulfill the requirement if there is a a requirement that he does * not fulfill. * * @param checkMessage the message component array */ public void setCheckMessage(BaseComponent[] checkMessage) { this.checkMessage = checkMessage; } /** * If the player's state - including his inventory, EXP etc. - is kept. * * @return if the player's state is kept */ public boolean isInventoryKept() { return keepInventory; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{requirement=" + requirement + "; player=" + player + "; keepInventory=" + keepInventory + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/requirement/RequirementDemandEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.requirement; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a {@link Requirement} is demanded from a player. *

* This is fired for all requirements, even for those that do not demand anything from the player. * * @author Daniel Saukel */ public class RequirementDemandEvent extends RequirementEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private Player player; private boolean keepInventory; public RequirementDemandEvent(Requirement requirement, Dungeon dungeon, Player player, boolean keepInventory) { super(requirement, dungeon); this.player = player; this.keepInventory = keepInventory; } /** * Returns the player who pays the requirement. * * @return the player */ public Player getPlayer() { return player; } /** * Sets the player who pays the requirement. * * @param player the player */ public void setPlayer(Player player) { this.player = player; } /** * If the player's state - including his inventory, EXP etc. - is kept. * * @return if the player's state is kept */ public boolean isInventoryKept() { return keepInventory; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{requirement=" + requirement + "; player=" + player + "; keepInventory=" + keepInventory + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/requirement/RequirementEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.requirement; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import org.bukkit.event.Event; /** * Superclass for events involving {@link Requirement}s. * * @author Daniel Saukel */ public abstract class RequirementEvent extends Event { protected Requirement requirement; protected Dungeon dungeon; public RequirementEvent(Requirement requirement, Dungeon dungeon) { this.requirement = requirement; this.dungeon = dungeon; } /** * Returns the dungeon involved in this event. * * @return the dungeon involved in this event */ public Dungeon getDungeon() { return dungeon; } /** * Returns the requirement involved in this event. * * @return the requirement involved in this event */ public Requirement getRequirement() { return requirement; } /** * Sets the requirement involved in this event. * * @param requirement the requirement */ public void setRequirement(Requirement requirement) { this.requirement = requirement; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/trigger/TriggerActionEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.trigger; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import java.util.List; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a {@link Trigger} is satisfied. * * @author Daniel Saukel */ public class TriggerActionEvent extends TriggerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private List fired; public TriggerActionEvent(Trigger trigger, List fired) { super(trigger); this.fired = fired; } /** * Returns a List of the fired listeners, e.g. the dungeon signs that are triggered because all their trigger expression is fully satisfied. * * @return a List of the fired listeners */ public List getFiredListeners() { return fired; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{trigger=" + trigger + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/trigger/TriggerEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.trigger; import de.erethon.dungeonsxl.api.trigger.Trigger; import org.bukkit.event.Event; /** * Superclass for events involving triggers. * * @author Daniel Saukel */ public abstract class TriggerEvent extends Event { protected Trigger trigger; protected TriggerEvent(Trigger trigger) { this.trigger = trigger; } /** * Returns the Trigger involved in this event. * * @return the trigger involved in this event */ public Trigger getTrigger() { return trigger; } /** * Sets the trigger involved in this event to the given value. * * @param trigger the trigger to set */ public void setTrigger(Trigger trigger) { this.trigger = trigger; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/trigger/TriggerRegistrationEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.trigger; import de.erethon.dungeonsxl.api.trigger.Trigger; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a {@link Trigger} is created. * * @author Daniel Saukel */ public class TriggerRegistrationEvent extends TriggerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; public TriggerRegistrationEvent(Trigger trigger) { super(trigger); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{trigger=" + trigger + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/trigger/TriggerUnregistrationEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.trigger; import de.erethon.dungeonsxl.api.trigger.Trigger; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a {@link Trigger} is unregistered. * * @author Daniel Saukel */ public class TriggerUnregistrationEvent extends TriggerEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; public TriggerUnregistrationEvent(Trigger trigger) { super(trigger); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{trigger=" + trigger + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/EditWorldEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.EditWorld; /** * Superclass for events involving DungeonsXL edit instances. * * @author Daniel Saukel */ public abstract class EditWorldEvent extends InstanceWorldEvent { protected EditWorldEvent(EditWorld editWorld) { super(editWorld); } /** * Returns the EditWorld involved in this event. * * @return the EditWorld involved in this event */ public EditWorld getEditWorld() { return (EditWorld) instance; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/EditWorldGenerateEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.EditWorld; import org.bukkit.event.HandlerList; /** * Fired after a dungeon world is generated. * * @author Daniel Saukel */ public class EditWorldGenerateEvent extends EditWorldEvent { private static final HandlerList handlers = new HandlerList(); public EditWorldGenerateEvent(EditWorld editWorld) { super(editWorld); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public String toString() { return getClass().getSimpleName() + "{instance=" + instance + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/EditWorldSaveEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.EditWorld; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when an edit world is saved. * * @author Daniel Saukel */ public class EditWorldSaveEvent extends EditWorldEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; public EditWorldSaveEvent(EditWorld editWorld) { super(editWorld); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{instance=" + instance + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/EditWorldUnloadEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.EditWorld; import org.bukkit.event.HandlerList; /** * Fired when an edit world is unloaded. * * @author Daniel Saukel */ public class EditWorldUnloadEvent extends InstanceWorldUnloadEvent { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private boolean save; public EditWorldUnloadEvent(EditWorld editWorld, boolean save) { super(editWorld); this.save = save; } /** * Returns if the world is saved. * * @return if the world is saved */ public boolean getSave() { return save; } /** * Sets if the world shall be saved. * * @param save if the world shall be saved */ public void setSave(boolean save) { this.save = save; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{instance=" + instance + "; save=" + save + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/GameWorldEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.world.GameWorld; /** * Superclass for events involving DungeonsXL game instances. * * @author Daniel Saukel */ public abstract class GameWorldEvent extends InstanceWorldEvent { private Dungeon dungeon; protected GameWorldEvent(GameWorld gameWorld, Dungeon dungeon) { super(gameWorld); this.dungeon = dungeon; } /** * Returns the GameWorld involved in this event. * * @return the GameWorld involved in this event. */ public GameWorld getGameWorld() { return (GameWorld) instance; } /** * Returns the dungeon the game instance is a part of. * * @return the dungeon the game instance is a part of */ public Dungeon getDungeon() { return dungeon; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/GameWorldStartGameEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.world.GameWorld; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when the game starts, that means when all players have triggered the ready sign. * * @author Daniel Saukel */ public class GameWorldStartGameEvent extends GameWorldEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private Game game; public GameWorldStartGameEvent(GameWorld gameWorld, Game game) { super(gameWorld, gameWorld.getDungeon()); this.game = game; } /** * Returns the game. * * @return the game */ public Game getGame() { return game; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{instance=" + instance + "; dungeon=" + getDungeon() + "; game=" + game + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/InstanceWorldEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.World; import org.bukkit.event.Event; /** * Superclass for events involving DungeonsXL instances. * * @author Daniel Saukel */ public abstract class InstanceWorldEvent extends Event { protected InstanceWorld instance; protected InstanceWorldEvent(InstanceWorld instance) { this.instance = instance; } /** * Returns the instance involved in this event. * * @return the instance involved in this event */ public InstanceWorld getInstance() { return instance; } /** * Returns the Bukkit world involved in this event. * * @return the Bukkit world involved in this event. */ public World getBukkitWorld() { return instance.getWorld(); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/InstanceWorldPostUnloadEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.ResourceWorld; import org.bukkit.event.HandlerList; /** * Fired after an instance world is unloaded. * * @author Daniel Saukel */ public class InstanceWorldPostUnloadEvent extends ResourceWorldEvent { private static final HandlerList handlers = new HandlerList(); private String instanceWorldName; public InstanceWorldPostUnloadEvent(ResourceWorld resource, String instanceWorldName) { super(resource); this.instanceWorldName = instanceWorldName; } /** * Returns the name the instance world had. * * @return the name the instance world had */ public String getInstanceWorldName() { return instanceWorldName; } /** * Returns if the unloaded instance was an edit world. * * @return if the unloaded instance was an edit world */ public boolean wasEditInstance() { return instanceWorldName.startsWith("DXL_Edit_"); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public String toString() { return getClass().getSimpleName() + "{resource=" + resource + "; instanceWorldName=" + instanceWorldName + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/InstanceWorldUnloadEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when an instance is unloaded. * * @author Daniel Saukel */ public class InstanceWorldUnloadEvent extends InstanceWorldEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; public InstanceWorldUnloadEvent(InstanceWorld instance) { super(instance); } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{instance=" + instance + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/ResourceWorldEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.ResourceWorld; import org.bukkit.event.Event; /** * Superclass for events involving DungeonsXL resource worlds. * * @author Daniel Saukel */ public abstract class ResourceWorldEvent extends Event { protected ResourceWorld resource; protected ResourceWorldEvent(ResourceWorld resource) { this.resource = resource; } /** * Returns the resource world involved in this event. * * @return the resource world involved in this event. */ public ResourceWorld getResource() { return resource; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/event/world/ResourceWorldInstantiateEvent.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.event.world; import de.erethon.dungeonsxl.api.world.ResourceWorld; import org.bukkit.event.Cancellable; import org.bukkit.event.HandlerList; /** * Fired when a {@link ResourceWorld} is instantiated. * * @author Daniel Saukel */ public class ResourceWorldInstantiateEvent extends ResourceWorldEvent implements Cancellable { private static final HandlerList handlers = new HandlerList(); private boolean cancelled; private String instanceWorldName; public ResourceWorldInstantiateEvent(ResourceWorld resource, String instanceWorldName) { super(resource); this.instanceWorldName = instanceWorldName; } /** * Returns if the loaded instance will be an edit world. * * @return if the loaded instance will be an edit world */ public boolean isEditInstance() { return instanceWorldName.startsWith("DXL_Edit_"); } /** * Returns the name the newly loaded Bukkit world is going to have. *

* Note that at this point no Bukkit World object for this world exists. * * @return the name the newly loaded Bukkit world is going to have */ public String getInstanceWorldName() { return instanceWorldName; } @Override public HandlerList getHandlers() { return handlers; } public static HandlerList getHandlerList() { return handlers; } @Override public boolean isCancelled() { return cancelled; } @Override public void setCancelled(boolean cancelled) { this.cancelled = cancelled; } @Override public String toString() { return getClass().getSimpleName() + "{resource=" + resource + "; instanceWorldName=" + instanceWorldName + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/mob/DungeonMob.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.mob; import de.erethon.xlib.mob.ExMob; import org.bukkit.entity.LivingEntity; /** * Wrapper for a mob spawned in a dungeon. * * @author Daniel Saukel */ public interface DungeonMob { /** * Returns the entity that is wrapped by this object. * * @return the entity that is wrapped by this object */ LivingEntity getEntity(); /** * Returns the XLib representation of the mob or null if it is spawned by an external plugin. * * @return the XLib representation of the mob or null if it is spawned by an external plugin */ ExMob getType(); /** * Returns the String used to identify this mob for example in the context of triggers. * * @return the String used to identify this mob for example in the context of triggers */ String getTriggerId(); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/mob/ExternalMobProvider.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.mob; import org.bukkit.Bukkit; import org.bukkit.Location; /** * Other plugins / libraries that can handle and spawn mobs. * * @author Daniel Saukel */ public interface ExternalMobProvider { /** * Returns the identifier used on mob signs to spawn mobs from this provider. * * @return the identifier used on mob signs to spawn mobs from this provider */ String getIdentifier(); /** * Returns the raw console spawn command of the provider. *

* This method is necessary for the default implementation of {@link #getCommand(String, String, double, double, double)}. * * @return the raw console spawn command of the provider */ String getRawCommand(); /** * Returns the console spawn command of the provider with values replaced to spawn the mob represented by the given String. *

* The default implementation uses %mob%, %world%, %x%, %y% and %z% as placeholders and alternatively %block_x% etc. if values without decimals are needed. *

* This method is used in the default implementation of {@link #summon(String, org.bukkit.Location)}. * * @param mob the mob identifier * @param world the game world * @param x the x coordinate * @param y the y coordinate * @param z the z coordinate * @return the command with replaced variables */ default String getCommand(String mob, String world, double x, double y, double z) { return getRawCommand().replace("%mob%", mob).replace("%world%", world) .replace("%x%", String.valueOf(x)).replace("%y%", String.valueOf(y)).replace("%z%", String.valueOf(z)) .replace("%block_x%", String.valueOf(Location.locToBlock(x))) .replace("%block_y%", String.valueOf(Location.locToBlock(y))) .replace("%block_z%", String.valueOf(Location.locToBlock(z))); } /** * Summons the mob. *

* The default implementation requires {@link #getCommand(String, String, double, double, double)} to be implemented. * * @param mob the mob identifier * @param location the location where the mob will be spawned */ default void summon(String mob, Location location) { Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), getCommand(mob, location.getWorld().getName(), location.getX(), location.getY(), location.getZ())); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/mob/MobSet.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.mob; import java.util.ArrayList; import java.util.List; import org.bukkit.entity.LivingEntity; /** * Mobs spawned through a mob sign are added to mob sets. This allows for all mobs in a set to be referred to together with the ID of the mob. * * @author Daniel Saukel */ public class MobSet { private String id; private List spawned; private int size; private int reserved; private int killed; public MobSet(String id) { this.id = id; } public String getId() { return id; } public int getSize() { return size; } public int getReserved() { return reserved; } public void allocate(int amount) { size += amount; reserved += amount; } public void initialize() { spawned = new ArrayList<>(size); } public int getKilled() { return killed; } public void spawn(LivingEntity entity) { spawned.add(entity); } public void kill(LivingEntity entity) { spawned.remove(entity); } public boolean checkTrigger(int amount) { return killed >= amount; } public boolean checkTrigger(double quota) { return killed / size >= quota; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/EditPlayer.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.dungeonsxl.api.world.EditWorld; /** * Represents a player in an edit instance. *

* All players in an edit world have one wrapper object that is an instance of EditPlayer. * * @author Daniel Saukel */ // Implementation-specific methods: poke public interface EditPlayer extends InstancePlayer { /** * Returns the {@link de.erethon.dungeonsxl.api.world.EditWorld} the player is editing. * * @return the {@link de.erethon.dungeonsxl.api.world.EditWorld} the player is editing */ EditWorld getEditWorld(); /** * Returns the lines of a sign the player has copied with a stick tool in an array with the length of four. * * @return the lines of a sign the player has copied with a stick tool in an array with the length of four */ String[] getCopiedLines(); /** * Sets the memorized sign lines. * * @param copiedLines the lines */ void setCopiedLines(String[] copiedLines); /** * Makes the player leave his group and dungeon. *

* This unloads the world if there are no editors left after this player leaves. */ @Override default void leave() { leave(true); } /** * Makes the player leave his group and dungeon. * * @param unloadIfEmpty whether the world is to be unloaded if, after this player leaves, no editors are left */ void leave(boolean unloadIfEmpty); /** * Makes the player leave the edit world without saving the progress. */ void escape(); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/GamePlayer.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.world.GameWorld; import org.bukkit.Location; import org.bukkit.entity.Wolf; /** * Represents a player in a game dungeon instance. *

* All players in a game world have one wrapper object that is an instance of GamePlayer. * * @author Daniel Saukel */ // Implementation-specific methods: isInTestMode, setReady, [wolf, group tag methods], finishFloor public interface GamePlayer extends InstancePlayer { /** * Returns the game the player's group plays. * * @return the game the player's group plays */ default Game getGame() { return getGroup().getGame(); } /** * Returns the game world of the player's group. * * @return the game world of the player's group */ default GameWorld getGameWorld() { return getGroup().getGameWorld(); } /** * Returns if the player is ready to start the game. *

* This is usually achieved by triggering a ready sign. * * @return if the player is ready to start the game */ boolean isReady(); /** * Returns if the player finished the game. *

* This is usually achieved by triggering an end sign. *

* It is used for both the end of a whole dungeon and the end of a floor. * * @return if the player finished the game */ boolean isFinished(); /** * Sets if the player finished their game. * * @param finished if the player finished the game */ void setFinished(boolean finished); /** * Returns the player's class or null if they have none. * * @return the player's class */ PlayerClass getPlayerClass(); /** * Sets and applies the given class. * * @param playerClass the class */ void setPlayerClass(PlayerClass playerClass); /** * Returns the location of the last checkpoint the player reached. * * @return the location of the last checkpoint the player reached */ Location getLastCheckpoint(); /** * Sets the location of the last checkpoint the player reached. *

* This is where the player respawns if they die and have -1 or >0 {@link #getLives() lives} left. * * @param checkpoint the checkpoint location */ void setLastCheckpoint(Location checkpoint); /** * Returns the saved time millis from when the player went offline. * * @return the saved time millis from when the player went offline */ long getOfflineTimeMillis(); /** * Sets the saved time millis from when the player went offline. * * @param time the time millis */ void setOfflineTimeMillis(long time); /** * Returns the original amount of lives the player had in the current game or -1 if lives aren't used. * * @return the original amount of lives the player had in the current game or -1 if lives aren't used */ int getInitialLives(); /** * Sets the original amount of lives the player had in the current game; -1 means lives aren't used. * * @param lives the amount of lives */ void setInitialLives(int lives); /** * Returns the lives the player has left or -1 if per player lives aren't used. * * @return the lives the player has left or -1 if per player lives aren't used */ int getLives(); /** * Sets the lives the player has left. *

* This is not to be used if the dungeon uses group lives. * * @param lives the lives */ void setLives(int lives); /** * Returns the player's wolf or null if he does not have one. * * @return the player's wolf or null if he does not have one * @deprecated More dynamic pet features might make this obsolete in the future. */ @Deprecated Wolf getWolf(); /** * Gives the player a wolf. * * @param wolf the wolf * @deprecated More dynamic pet features might make this obsolete in the future. */ @Deprecated void setWolf(Wolf wolf); /** * Returns if the player is stealing another group's flag. * * @return if the player is stealing another group's flag */ boolean isStealingFlag(); /** * Returns the group whose flag the player robbed; null if the player isn't stealing any. * * @return the group whose flag the player robbed; null if the player isn't stealing any */ PlayerGroup getRobbedGroup(); /** * Sets the player to be stealing the team flag of the given group. * * @param group the group */ void setRobbedGroup(PlayerGroup group); /** * Scores a point. */ void captureFlag(); /** * Makes the player leave his group and dungeon. *

* This sends default messages to the player. */ @Override default void leave() { leave(true); } /** * Makes the player leave his group and dungeon. * * @param sendMessages if default messages shall be sent to the player */ void leave(boolean sendMessages); /** * Treats the player as if they lost their last life and kicks them from the dungeon. */ void kill(); /** * Sets the player to be ready to start the dungeon game, like when a ready sign is triggered. *

* If all other players in the group are already {@link #isReady() ready}, the game is started. * * @return if the game has been started. */ boolean ready(); /** * Respawns the player. Also teleports DXL pets if there are any. */ void respawn(); /** * The player finishs the current game. *

* This sends default messages to the player. */ default void finish() { finish(true); } /** * The player finishs the current game. * * @param sendMessages if default messages shall be sent to the player */ void finish(boolean sendMessages); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/GlobalPlayer.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.player.PlayerWrapper; import java.util.List; import org.bukkit.Location; import org.bukkit.inventory.ItemStack; /** * Represents a player anywhere on the server. *

* All players on the server, including the ones in dungeons, have one wrapper object that is an instance of GlobalPlayer. *

* Do not cache this for the whole runtime (or use {@link de.erethon.xlib.player.PlayerCollection}). The object may be deleted and replaced with an object of * the appropriate type when the player enters or leaves an instance. * * @author Daniel Saukel */ // Implementation-specific methods: getters and setters: data, portal, cached item, announcer; startTutorial public interface GlobalPlayer extends PlayerWrapper { /** * Returns the player's group. * * @return the player's group. */ PlayerGroup getGroup(); /** * Returns if the player uses the built-in group chat. * * @return if the player uses the built-in group chat */ boolean isInGroupChat(); /** * Sets if the player uses the built-in group chat. * * @param groupChat if the player shall use the built-in group chat */ void setInGroupChat(boolean groupChat); /** * Returns if the player may read messages from the built-in group chat. * * @return if the player may read messages from the built-in group chat */ boolean isInChatSpyMode(); /** * Sets if the player may read messages from the built-in group chat. * * @param chatSpyMode if the player may read messages from the built-in group chat */ void setInChatSpyMode(boolean chatSpyMode); /** * Checks if the player has the given permission. * * @param permission the permission * @return if the player has the given permission */ boolean hasPermission(String permission); /** * Returns the reward items the player gets after leaving the dungeon. * * @return the reward items the player gets after leaving the dungeon */ List getRewardItems(); /** * Sets the reward items the player gets after leaving the dungeon. * * @param rewardItems the reward items the player gets after leaving the dungeon */ void setRewardItems(List rewardItems); /** * Returns if the player has any reward items left. * * @return if the player has any reward items left */ boolean hasRewardItemsLeft(); /** * Returns if the player is currently breaking a global protection (=using /dxl break). * * @return if the player is currently breaking a global protection (=using /dxl break) */ boolean isInBreakMode(); /** * Sets the player into or out of break mode; see {@link #isInBreakMode()}. * * @param breakMode if the player may break global protections */ void setInBreakMode(boolean breakMode); /** * Sends a message to the player. *

* Supports color codes. * * @param message the message to send */ default void sendMessage(String message) { MessageUtil.sendMessage(getPlayer(), message); } /** * Respawns the player at the location defined by the game rules or his old position before he was in a dungeon. * * @param gameFinished if the game was finished */ void reset(boolean gameFinished); /** * Respawns the player at the given location. * * @param tpLoc the location where the player shall respawn * @param keepInventory if the saved status shall be reset */ void reset(Location tpLoc, boolean keepInventory); /** * Performs a requirement check for the given dungeon. *

* This method might send messages to the player to inform him that he does not fulfill them. * * @param dungeon the dungeon to check * @return if the player fulfills the requirements or may bypass them */ boolean checkRequirements(Dungeon dungeon); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/GroupAdapter.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.dungeonsxl.api.DungeonsAPI; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import org.bukkit.entity.Player; /** * Implement and register in order to track a group. *

* See implementation classes in de.erethon.dungeonsxl.player.groupadapter for reference. * * @param the external group object * @author Daniel Saukel */ public abstract class GroupAdapter { protected DungeonsAPI dxl; protected Map groups = new HashMap<>(); /** * @param dxl the DungeonsAPI instance */ protected GroupAdapter(DungeonsAPI dxl) { this.dxl = dxl; } /** * Creates a dungeon group {@link #areCorresponding(PlayerGroup, Object) corresponding} with the external group. * * @param eGroup the external group * @return a dungeon group {@link #areCorresponding(PlayerGroup, Object) corresponding} with the external group */ public abstract PlayerGroup createDungeonGroup(T eGroup); /** * Returns the dungeon group {@link #areCorresponding(PlayerGroup, Object) corresponding} with the external group or null of none exists. * * @param eGroup the external group * @return the dungeon group {@link #areCorresponding(PlayerGroup, Object) corresponding} with the external group */ public PlayerGroup getDungeonGroup(T eGroup) { if (eGroup == null) { return null; } for (Entry entry : groups.entrySet()) { if (entry.getValue().equals(eGroup)) { return entry.getKey(); } } return null; } /** * Returns the external group {@link #areCorresponding(PlayerGroup, Object) corresponding} with the dungeon group. * * @param dGroup the dungeon group * @return the external group {@link #areCorresponding(PlayerGroup, Object) corresponding} with the dungeon group */ public T getExternalGroup(PlayerGroup dGroup) { return groups.get(dGroup); } /** * Returns the dungeon group that mirrors the external group. *

* Creates a dungeon group if none exists and if the party has no more online members than maxSize. * * @param eGroup the dungeon group * @param maxSize the maximum size of the group * @return the dungeon group that mirrors the dungeon group */ public PlayerGroup getOrCreateDungeonGroup(T eGroup, int maxSize) { if (eGroup == null) { return null; } PlayerGroup dGroup = getDungeonGroup(eGroup); if (dGroup == null && getGroupOnlineSize(eGroup) <= maxSize) { dGroup = createDungeonGroup(eGroup); } return dGroup; } /** * Returns the dungeon group that mirrors the external group. *

* Creates a dungeon group if none exists. * * @param eGroup the dungeon group * @return the dungeon group that mirrors the dungeon group */ public PlayerGroup getOrCreateDungeonGroup(T eGroup) { if (eGroup == null) { return null; } PlayerGroup dGroup = getDungeonGroup(eGroup); if (dGroup == null) { dGroup = createDungeonGroup(eGroup); } return dGroup; } /** * Returns the external group of the given group member. * * @param member the group member * @return the external group of the given group member */ public abstract T getExternalGroup(Player member); /** * Returns the amount of members in the external group who are online. * * @param eGroup the external group * @return the amount of members in the external group who are online */ public abstract int getGroupOnlineSize(T eGroup); /** * Checks if two groups are corresponding. *

* Corresponding groups are groups that should be regarded as one from the perspective of a player. *

* Two null values are regarded as corresponding. * * @param dGroup the dungeon group * @param eGroup the external group * @return if the two groups are corresponding */ public boolean areCorresponding(PlayerGroup dGroup, T eGroup) { if (dGroup == null || eGroup == null) { return false; } T dExternal = groups.get(dGroup); return dExternal != null && eGroup.equals(dExternal); } /** * Returns if the player is a member of any external group. * * @param player the player * @return if the player is a member of any external group */ public boolean isExternalGroupMember(Player player) { return getExternalGroup(player) != null; } /** * Returns if the player is a member of the external group. * * @param eGroup the external group * @param player the player * @return if the player is a member of the external group */ public abstract boolean isExternalGroupMember(T eGroup, Player player); /** * Clears the external / dungeon group references. */ public void clear() { groups.clear(); } /** * Removes the external / dungeon group reference from the cache. * * @param dGroup the DXL group that belongs to an external group. */ public void removeReference(PlayerGroup dGroup) { groups.remove(dGroup); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/InstancePlayer.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.World; /** * Represents a player in an instance. *

* All players in a world instantiated by DungeonsXL, have one wrapper object that is an instance of InstancePlayer. * * @author Daniel Saukel */ // Implementation-specific methods: setWorld, clearPlayerData, delete, chat, update public interface InstancePlayer extends GlobalPlayer { /** * The world of the instance, where the player is supposed to be. * * @return the world of the instance */ World getWorld(); /** * The world of the instance, where the player is supposed to be. * * @return the instance world */ InstanceWorld getInstanceWorld(); /** * Makes the player leave his group and dungeon. */ void leave(); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/PlayerCache.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.xlib.util.Registry; import java.util.ArrayList; import java.util.Collection; import java.util.UUID; import java.util.function.Predicate; import org.bukkit.entity.Player; /** * Stores information on players in and out of dungeons. * * @author Daniel Saukel */ public class PlayerCache extends Registry { /** * Returns the {@link GlobalPlayer} that represents the player with the given UUID. * * @param uuid a player's UUID to check * @return the {@link GlobalPlayer} that represents the given player both for players in dungeons or outside; null if they are offline */ public GlobalPlayer get(UUID uuid) { return getFirstIf(p -> p.getUniqueId().equals(uuid)); } /** * Returns the {@link InstancePlayer} that represents the given player. * * @param player the player to check * @return the {@link InstancePlayer} that represents the given player; null if the player is neither editing nor in a game. */ public InstancePlayer getInstancePlayer(Player player) { return getFirstInstancePlayerIf(p -> p.getPlayer() == player); } /** * Returns the {@link EditPlayer} that represents the given player. * * @param player the player to check * @return the {@link EditPlayer} that represents the given player; null if the player is not editing */ public EditPlayer getEditPlayer(Player player) { return getFirstEditPlayerIf(p -> p.getPlayer() == player); } /** * Returns the {@link GamePlayer} that represents the given player. * * @param player the player to check * @return the {@link GamePlayer} that represents the given player; null if the player is not in a game */ public GamePlayer getGamePlayer(Player player) { return getFirstGamePlayerIf(p -> p.getPlayer() == player); } /** * Returns the first {@link InstancePlayer} that satisfies the given predicate. * * @param predicate the predicate to check * @return the first {@link InstancePlayer} that satisfies the given predicate */ public InstancePlayer getFirstInstancePlayerIf(Predicate predicate) { for (GlobalPlayer element : elements.values()) { if (!(element instanceof InstancePlayer)) { continue; } InstancePlayer instancePlayer = (InstancePlayer) element; if (predicate.test(instancePlayer)) { return instancePlayer; } } return null; } /** * Returns the first {@link EditPlayer} that satisfies the given predicate. * * @param predicate the predicate to check * @return the first {@link EditPlayer} that satisfies the given predicate */ public EditPlayer getFirstEditPlayerIf(Predicate predicate) { for (GlobalPlayer element : elements.values()) { if (!(element instanceof EditPlayer)) { continue; } EditPlayer editPlayer = (EditPlayer) element; if (predicate.test(editPlayer)) { return editPlayer; } } return null; } /** * Returns the first {@link GamePlayer} that satisfies the given predicate. * * @param predicate the predicate to check * @return the first {@link GamePlayer} that satisfies the given predicate */ public GamePlayer getFirstGamePlayerIf(Predicate predicate) { for (GlobalPlayer element : elements.values()) { if (!(element instanceof GamePlayer)) { continue; } GamePlayer gamePlayer = (GamePlayer) element; if (predicate.test(gamePlayer)) { return gamePlayer; } } return null; } /** * Returns all {@link InstancePlayer}s that satisfy the given predicate. * * @param predicate the predicate to check * @return all {@link InstancePlayer} that satisfy the given predicate */ public Collection getAllInstancePlayersIf(Predicate predicate) { Collection checked = new ArrayList<>(); for (GlobalPlayer element : elements.values()) { if (!(element instanceof InstancePlayer)) { continue; } InstancePlayer instancePlayer = (InstancePlayer) element; if (predicate.test(instancePlayer)) { checked.add(instancePlayer); } } return checked; } /** * Returns all {@link EditPlayer}s that satisfy the given predicate. * * @param predicate the predicate to check * @return all {@link EditPlayer}s that satisfy the given predicate */ public Collection getAllEditPlayersIf(Predicate predicate) { Collection checked = new ArrayList<>(); for (GlobalPlayer element : elements.values()) { if (!(element instanceof EditPlayer)) { continue; } EditPlayer editPlayer = (EditPlayer) element; if (predicate.test(editPlayer)) { checked.add(editPlayer); } } return checked; } /** * Returns all {@link GamePlayer}s that satisfy the given predicate. * * @param predicate the predicate to check * @return all {@link GamePlayer} that satisfy the given predicate */ public Collection getAllGamePlayersIf(Predicate predicate) { Collection checked = new ArrayList<>(); for (GlobalPlayer element : elements.values()) { if (!(element instanceof GamePlayer)) { continue; } GamePlayer gamePlayer = (GamePlayer) element; if (predicate.test(gamePlayer)) { checked.add(gamePlayer); } } return checked; } /** * Returns all {@link InstancePlayer}s. * * @return all {@link InstancePlayer}s */ public Collection getAllInstancePlayers() { Collection checked = new ArrayList<>(); for (GlobalPlayer element : elements.values()) { if (element instanceof InstancePlayer) { checked.add((InstancePlayer) element); } } return checked; } /** * Returns all {@link EditPlayer}s. * * @return all {@link EditPlayer}s */ public Collection getAllEditPlayers() { Collection checked = new ArrayList<>(); for (GlobalPlayer element : elements.values()) { if (element instanceof EditPlayer) { checked.add((EditPlayer) element); } } return checked; } /** * Returns all {@link GamePlayer}s. * * @return all {@link GamePlayer}s */ public Collection getAllGamePlayers() { Collection checked = new ArrayList<>(); for (GlobalPlayer element : elements.values()) { if (element instanceof GamePlayer) { checked.add((GamePlayer) element); } } return checked; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/PlayerClass.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.xlib.XLib; import java.io.File; import java.util.ArrayList; import java.util.List; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.inventory.ItemStack; /** * Represents a class and a class script. * * @author Daniel Saukel */ public class PlayerClass { private String name; private List items = new ArrayList<>(); private boolean dog; /** * Creates a PlayerClass from a class YAML file. The name is taken from the file name. * * @param xlib the XLib instance * @param file the class config file */ public PlayerClass(XLib xlib, File file) { this(xlib, file.getName().substring(0, file.getName().length() - 4), YamlConfiguration.loadConfiguration(file)); } /** * Creates a PlayerClass from the given class config. * * @param xlib the XLib instance * @param name the class name * @param config the config */ public PlayerClass(XLib xlib, String name, FileConfiguration config) { this.name = name; if (config.contains("items")) { items = xlib.deserializeStackList(config, "items"); } if (config.contains("dog")) { dog = config.getBoolean("dog"); } } public PlayerClass(String name, List items, boolean dog) { this.items = items; this.name = name; this.dog = dog; } /** * Returns the name of the class. * * @return the name of the class */ public String getName() { return name; } /** * Returns the list of the items this class gives to a player. * * @return the list of the the items this class gives to a player */ public List getItems() { return items; } /** * Adds the given item to this class. * * @param itemStack the ItemStack to add */ public void addItem(ItemStack itemStack) { items.add(itemStack); } /** * Removes the given item from this class. * * @param itemStack the ItemStack to remove */ public void removeItem(ItemStack itemStack) { items.remove(itemStack); } /** * Returns if the class gives the player a dog. * * @return if the class has a dog * @deprecated More dynamic pet features might make this obsolete in the future. */ @Deprecated public boolean hasDog() { return dog; } /** * Sets if the class gives the player a dog. * * @param dog if the class shall give the player a dog * @deprecated More dynamic pet features might make this obsolete in the future. */ @Deprecated public void setDog(boolean dog) { this.dog = dog; } @Override public String toString() { return getClass().getSimpleName() + "{name=" + name + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/player/PlayerGroup.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.player; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.player.PlayerCollection; import java.util.List; import org.bukkit.ChatColor; import org.bukkit.DyeColor; import org.bukkit.entity.Player; /** * Represents a group of players provided by DungeonsXL. * * @author Daniel Saukel */ // Implementation-specific methods: setDungeon, setPlaying, [color, unplayed floor, floor count methods], isEmpty, isCustom, teleport, // finish, finishFloor, startGame, winGame, requirements methods public interface PlayerGroup { /** * Links different color types together. */ public enum Color { BLACK(ChatColor.BLACK, DyeColor.BLACK, VanillaItem.BLACK_WOOL), DARK_GRAY(ChatColor.DARK_GRAY, DyeColor.GRAY, VanillaItem.GRAY_WOOL), LIGHT_GRAY(ChatColor.GRAY, DyeColor.valueOf(Version.isAtLeast(Version.MC1_13) ? "LIGHT_GRAY" : "SILVER"), VanillaItem.LIGHT_GRAY_WOOL), WHITE(ChatColor.WHITE, DyeColor.WHITE, VanillaItem.WHITE_WOOL), DARK_GREEN(ChatColor.DARK_GREEN, DyeColor.GREEN, VanillaItem.GREEN_WOOL), LIGHT_GREEN(ChatColor.GREEN, DyeColor.LIME, VanillaItem.LIME_WOOL), CYAN(ChatColor.DARK_AQUA, DyeColor.CYAN, VanillaItem.CYAN_WOOL), DARK_BLUE(ChatColor.DARK_BLUE, DyeColor.BLUE, VanillaItem.BLUE_WOOL), LIGHT_BLUE(ChatColor.AQUA, DyeColor.LIGHT_BLUE, VanillaItem.LIGHT_BLUE_WOOL), PURPLE(ChatColor.DARK_PURPLE, DyeColor.PURPLE, VanillaItem.PURPLE_WOOL), MAGENTA(ChatColor.LIGHT_PURPLE, DyeColor.MAGENTA, VanillaItem.MAGENTA_WOOL), DARK_RED(ChatColor.DARK_RED, DyeColor.BROWN, VanillaItem.BROWN_WOOL), LIGHT_RED(ChatColor.RED, DyeColor.RED, VanillaItem.RED_WOOL), ORANGE(ChatColor.GOLD, DyeColor.ORANGE, VanillaItem.ORANGE_WOOL), YELLOW(ChatColor.YELLOW, DyeColor.YELLOW, VanillaItem.YELLOW_WOOL), PINK(ChatColor.BLUE, DyeColor.PINK, VanillaItem.PINK_WOOL); private ChatColor chat; private DyeColor dye; private VanillaItem woolMaterial; Color(ChatColor chat, DyeColor dye, VanillaItem woolMaterial) { this.chat = chat; this.dye = dye; this.woolMaterial = woolMaterial; } /** * Returns the ChatColor. * * @return the ChatColor */ public ChatColor getChatColor() { return chat; } /** * Returns the DyeColor. * * @return the DyeColor */ public DyeColor getDyeColor() { return dye; } /** * Returns the RGB value. * * @return the RGB value */ public int getRGBColor() { return dye.getColor().asRGB(); } /** * Returns the wool material. * * @return the wool material */ public VanillaItem getWoolMaterial() { return woolMaterial; } /** * Returns the GroupColor matching the ChatColor or null if none exists. * * @param color the ChatColor to check * @return the GroupColor matching the ChatColor or null if none exists */ public static Color getByChatColor(ChatColor color) { for (Color groupColor : values()) { if (groupColor.chat == color) { return groupColor; } } return null; } /** * Returns the GroupColor matching the DyeColor or null if none exists. * * @param color the DyeColor to check * @return the GroupColor matching the DyeColor or null if none exists. */ public static Color getByDyeColor(DyeColor color) { for (Color groupColor : values()) { if (groupColor.dye == color) { return groupColor; } } return null; } /** * Returns the GroupColor matching the wool material or null if none exists. * * @param wool the wool material to check * @return the GroupColor matching the wool material or null if none exists */ public static Color getByWoolType(ExItem wool) { for (Color groupColor : values()) { if (groupColor.woolMaterial == wool) { return groupColor; } } return null; } } /** * Returns the ID. * * @return the ID */ int getId(); /** * Returns the formatted name. *

* This is the name used e.g. in messages. * * @return the formatted name */ String getName(); /** * Returns the raw, unformatted name. *

* This is the name used e.g. in command arguments. * * @return the raw, unformatted name */ String getRawName(); /** * Sets the name. * * @param name the name */ void setName(String name); /** * Sets the name to a default value taken from the color. *

* In the default implementation, this is nameOfTheColor#{@link #getId()} * * @param color the color */ default void setName(Color color) { setName(color.toString()); } /** * The player who has permission to manage the group. * * @return the player who has permission to manage the group */ Player getLeader(); /** * Sets the leader to another group member. * * @param player the new leader */ void setLeader(Player player); /** * Returns a PlayerCollection of the group members * * @return a PlayerCollection of the group members */ PlayerCollection getMembers(); /** * Adds a player to the group. *

* The default implemenation calls {@link #addMember(Player, boolean)} with messages set to true. * * @param player the player to add */ default void addMember(Player player) { addMember(player, true); } /** * Adds a player to the group. * * @param player the player to add * @param message if messages shall be sent */ void addMember(Player player, boolean message); /** * Removes a player from the group. *

* The default implemenation calls {@link #removeMember(Player, boolean)} with messages set to true. * * @param player the player to add */ default void removeMember(Player player) { removeMember(player, true); } /** * Removes a player from the group. * * @param player the player to add * @param message if messages shall be sent */ void removeMember(Player player, boolean message); /** * Returns a PlayerCollection of the players who are invited to join the group but did not yet do so. * * @return a PlayerCollection of the players who are invited to join the group but did not yet do so */ PlayerCollection getInvitedPlayers(); /** * Invites a player to join the group. * * @param player the player to invite * @param message if messages shall be sent */ void addInvitedPlayer(Player player, boolean message); /** * Removes an invitation priviously made for a player to join the group. * * @param player the player to uninvite * @param message if messages shall be sent */ void removeInvitedPlayer(Player player, boolean message); /** * Removes all invitations for players who are not online. */ void clearOfflineInvitedPlayers(); /** * Returns the game of the game world the group is in. * * @return the game of the game world the group is in. */ Game getGame(); /** * Returns the game world the group is in. * * @return the game world the group is in */ default GameWorld getGameWorld() { return getGame() != null ? getGame().getWorld() : null; } /** * Returns the dungeon the group is playing or has remembered to play next. *

* The latter is for example used when a group is created by a group sign sothat a portal or the auto-join function knows where to send the group. * * @return the dungeon the group is playing or has remembered to play next */ Dungeon getDungeon(); /** * Returns if the group is already playing its remembered {@link #getDungeon() dungeon}. * * @return if the group is already playing its remembered {@link #getDungeon() dungeon} */ boolean isPlaying(); /** * Returns the rewards that are memorized for the group. These are given when the game is finished. * * @return the rewards */ List getRewards(); /** * Memorizes the given reward for the group. These are given when the game is finished. * * @param reward the reward */ void addReward(Reward reward); /** * Removes the given reward. * * @param reward the reward */ void removeReward(Reward reward); /** * Returns the score number, which is used for capture the flag and similar game types. * * @return the score number */ int getScore(); /** * Sets the score of this group to a new value. * * @param score the value */ void setScore(int score); /** * Returns the initial amount of lives or -1 if group lives are not used. * * @return the initial amount of lives or -1 if group lives are not used */ int getInitialLives(); /** * Sets the initial amount of lives. *

* The value must be >=0 or -1, which means unlimited lives. * * @param lives the new amount of lives known as the initial amount */ void setInitialLives(int lives); /** * Returns the amount of lives the group currently has left or -1 if group lives are not used. * * @return the amount of lives the group currently has left or -1 if group lives are not used */ int getLives(); /** * Sets the amount of lives the group currently has left. *

* The value must be >=0 or -1, which means unlimited lives. * * @param lives the amount of lives the group currently has left */ void setLives(int lives); /** * Returns true if all players of the group have finished the game; false if not. * * @return true if all players of the group have finished the game; false if not */ boolean isFinished(); /** * Disbands the group. */ void delete(); /** * Sends a message to all players in the group. *

* Supports color codes. * * @param message the message to sent * @param except Players who shall not receive the message */ default void sendMessage(String message, Player... except) { members: for (Player player : getMembers().getOnlinePlayers()) { if (!player.isOnline()) { continue; } for (Player nope : except) { if (player == nope) { continue members; } } MessageUtil.sendMessage(player, message); } } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/AbstractDSign.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import java.util.ArrayList; import java.util.List; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.block.Sign; /** * Skeletal implementation of {@link DungeonSign}. * * @author Daniel Saukel */ public abstract class AbstractDSign implements DungeonSign { public static final String ERROR_0 = ChatColor.DARK_RED + "## ERROR ##"; public static final String ERROR_1 = ChatColor.WHITE + "Please"; public static final String ERROR_2 = ChatColor.WHITE + "contact an"; public static final String ERROR_3 = ChatColor.WHITE + "Admin!"; protected DungeonsAPI api; private Sign sign; private String[] lines; private InstanceWorld instance; String worldName; private LogicalExpression triggerExpression; private List triggers; private boolean initialized; private boolean erroneous; protected AbstractDSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { this.api = api; this.sign = sign; this.lines = lines; this.instance = instance; worldName = instance.getWorld().getName(); } @Override public Sign getSign() { return sign; } @Override public String[] getLines() { return lines; } @Override public Location getLocation() { return sign.getLocation(); } @Override public EditWorld getEditWorld() { return instance instanceof EditWorld ? (EditWorld) instance : null; } @Override public GameWorld getGameWorld() { return instance instanceof GameWorld ? (GameWorld) instance : null; } @Override public LogicalExpression getTriggerExpression() { return triggerExpression; } @Override public List getTriggers() { if (triggers != null) { return triggers; } List contents = triggerExpression.getContents(true); triggers = new ArrayList<>(contents.size()); contents.forEach(e -> triggers.add(e.toTrigger(api, this, false))); return triggers; } @Override public boolean isInitialized() { return initialized; } @Override public void updateTriggers(Trigger lastFired) { if (isErroneous()) { return; } if (!triggerExpression.isSatisfied()) { return; } try { trigger(lastFired != null ? lastFired.getTriggeringPlayer() : null); } catch (Exception exception) { markAsErroneous("An error occurred while triggering a sign of the type " + getName() + ". This is not a user error. Please report the following stacktrace to the developer of the plugin:"); exception.printStackTrace(); } } @Override public boolean setToAir() { sign.getBlock().setType(VanillaItem.AIR.getMaterial()); return true; } @Override public boolean isErroneous() { return erroneous; } @Override public void markAsErroneous(String reason) { erroneous = true; sign.setLine(0, ERROR_0); sign.setLine(1, ERROR_1); sign.setLine(2, ERROR_2); sign.setLine(3, ERROR_3); sign.update(); MessageUtil.log(api, "&4A sign at &6" + sign.getX() + ", " + sign.getY() + ", " + sign.getZ() + "&4 is erroneous!"); MessageUtil.log(api, getName() + ": " + reason); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/Button.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * A sign that performs a specific action every time it is triggered. It can have, but typically does not have a state. Consider using {@link Deactivatable} for * signs that change themselves when they are triggered. *

* For example, a classes sign with the default interact trigger sets your class every time you punch it. * * @author Daniel Saukel */ public abstract class Button extends AbstractDSign { protected Button(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } /** * When the sign is triggered without one particular player being the cause. *

* Note that the default implementation of {@link #push(org.bukkit.entity.Player)} assumes that the sign does not need player specific behavior and * simply calls this method, while the default implementation of this method assumes that the sign should perform {@link #push(org.bukkit.entity.Player)} * for each player in the game world. This leaves a button sign with a stackoverflow if not one of both methods at least is overriden. Consider using a * {@link Passive} sign instead if you need a sign that simply marks places and ignores being triggered. */ public void push() { getGameWorld().getPlayers().forEach(p -> push(p.getPlayer())); } /** * When the sign is triggered. *

* This is the default {@link #trigger(org.bukkit.entity.Player)} behavior. *

* Note that the default implementation of this method assumes that the sign does not need player specific behavior and simply calls {@link #push()}, * while the default implementation of {@link #push()} assumes that the sign should perform {@link #push(org.bukkit.entity.Player)} for each player in the * game world. This leaves a button sign with a stackoverflow if not one of both methods at least is overriden. Consider using a {@link Passive} sign * instead if you need a sign that simply marks places and ignores being triggered. * * @param player the player who triggered the sign * @return if the action is done successfully */ public boolean push(Player player) { push(); return true; } /** * This is the same as {@link #push(org.bukkit.entity.Player)}. * * @param player the player who triggered the sign or null if no one in particular triggered it */ @Override public void trigger(Player player) { if (player != null) { push(player); } else { push(); } } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/Deactivatable.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.xlib.player.PlayerCollection; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * A {@link DungeonSign} that changes its state when triggered. * * @author Daniel Saukel */ public abstract class Deactivatable extends AbstractDSign { protected boolean active; protected PlayerCollection playersActivated = new PlayerCollection(); protected Deactivatable(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } /** * Sets the state to active. *

* Note that the default implementation of {@link #activate(org.bukkit.entity.Player)} assumes that the sign does not need player specific behavior and * simply calls this method, while the default implementation of this method assumes that the sign should perform * {@link #activate(org.bukkit.entity.Player)} for each player in the game world. This leaves a button sign with a stackoverflow if not one of both methods * at least is overriden. Consider using a {@link Passive} sign instead if you need a sign that simply marks places and ignores being triggered. An * implementation that does not need player specific behavior should set {@link #active} to true. */ public void activate() { getGameWorld().getPlayers().forEach(p -> activate(p.getPlayer())); } /** * Sets the state to active for the given player. *

* Note that the default implementation of this method assumes that the sign does not need player specific behavior and simply calls {@link #activate()}, * while the default implementation of {@link #activate()} assumes that the sign should perform {@link #activate(org.bukkit.entity.Player)} for each player * in the game world. This leaves a deactivatable sign with a stackoverflow if not one of both methods at least is overriden. Consider using a * {@link Passive} sign instead if you need a sign that simply marks places and ignores being triggered. An implementation that needs player specific * behavior should add the player to the {@link #playersActivated} collection. * * @param player the player * @return if the action was successful */ public boolean activate(Player player) { activate(); return true; } /** * Sets the state to inactive. *

* Note that the default implementation of {@link #deactivate(org.bukkit.entity.Player)} assumes that the sign does not need player specific behavior and * simply calls this method, while the default implementation of this method assumes that the sign should perform * {@link #deactivate(org.bukkit.entity.Player)} for each player in the game world. This leaves a button sign with a stackoverflow if not one of both * methods at least is overriden. Consider using a {@link Passive} sign instead if you need a sign that simply marks places and ignores being triggered. An * implementation that does not need player specific behavior should set {@link #active} to false. */ public void deactivate() { getGameWorld().getPlayers().forEach(p -> deactivate(p.getPlayer())); } /** * Sets the state to inactive for the given player. *

* Note that the default implementation of this method assumes that the sign does not need player specific behavior and simply calls * {@link #deactivate()}, while the default implementation of {@link #deactivate()} assumes that the sign should perform * {@link #deactivate(org.bukkit.entity.Player)} for each player in the game world. This leaves a deactivatable sign with a stackoverflow if not one of both * methods at least is overriden. Consider using a {@link Passive} sign instead if you need a sign that simply marks places and ignores being triggered. An * implementation that needs player specific behavior should remove the player from the {@link #playersActivated} collection. * * @param player the player * @return if the action was successful */ public boolean deactivate(Player player) { deactivate(); return true; } /** * Returns if the sign is currently in its activated state. *

* This might not be meaningful if the sign uses {@link #isActive(org.bukkit.entity.Player)}. * * @return if the sign is currently in its activated state */ public boolean isActive() { return active; } /** * Returns if the sign is activated for the given player. *

* Note that the default implementation of this method assumes that the sign does not need player specific behavior and simply calls {@link #isActive()}. * An implementation that needs player specific behavior should check if the {@link #playersActivated} collection contains the player. * * @param player the player * @return if the sign is activated for the given player */ public boolean isActive(Player player) { return isActive(); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/DungeonSign.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.world.EditWorld; import org.bukkit.block.Sign; /** * Interface for all dungeon signs. * * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public interface DungeonSign extends TriggerListener { /** * Returns the name to identify the sign. * * @return the name */ String getName(); /** * Returns the permission node that is required to build a sign of this type. * * @return the build permission */ String getBuildPermission(); /** * Returns if the sign gets initialized when the dungeon is loaded instead of when the game starts. * * @return if the sign gets initialized when the dungeon is loaded instead of when the game starts */ boolean isOnDungeonInit(); /** * Returns if the sign block is breakable after the initialization. * * @return if the sign block is breakable after the initialization */ boolean isProtected(); /** * Returns true if the fourth line of the sign that usually contains the trigger is used differently. * * @deprecated This is overriden by interact signs, but it is strongly advised to stick to the convention to fetch data only from the second and third line. * * @return true if the fourth line of the sign that usually contains the trigger is used differently */ @Deprecated default boolean isTriggerLineDisabled() { return false; } /** * Returns if the block type of the sign is set to air after the initialization. * * @return if the block type of the sign is set to air after the initialization */ boolean isSetToAir(); /** * Returns the sign that represents event point. *

* Use {@link #getLines()} instead to read the raw data of this dungeon sign. * * @return the sign that represents event point */ Sign getSign(); /** * Returns the raw line of this sign at the given index. *

* These lines might not be the physical lines of {@link #getSign()}. * * @param index the line index (0-3) * @return the raw lines of this sign in an array with 4 elements */ default String getLine(int index) { return getLines()[index]; } /** * Returns the raw lines of this sign in an array with 4 elements. *

* These lines might not be the physical lines of {@link #getSign()}. * * @return the raw lines of this sign in an array with 4 elements */ String[] getLines(); /** * Returns the edit world this sign is in; null if this is in a game world. * * @return the edit world this sign is in; null if this is in a game world */ EditWorld getEditWorld(); /** * Sets the sign to air if it is not erroneous and if its type requires this. *

* Signs are usually to be set to air upon initialization, but this is not done automatically because some signs need different behavior. Script signs for * example are not set to air because this would override whatever a block sign in its script does. * * @return if the sign type was set to air */ boolean setToAir(); /** * Returns if the sign is valid. *

* A sign is invalid when it lacks needed parameters or if illegal arguments have been entered. * * @return if the sign is valid */ boolean validate(); /** * Returns if the sign is erroneous. * * @return if the sign is erroneous */ boolean isErroneous(); /** * Set a placeholder to show that the sign is setup incorrectly. * * @param reason the reason why the sign is marked as erroneous */ void markAsErroneous(String reason); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/Passive.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * A sign that does not do anything on its own. Its function is mostly to mark locations or blocks, like lobby or bed signs. * * @author Daniel Saukel */ public abstract class Passive extends AbstractDSign { protected Passive(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } /** * Does nothing. * * @param lastFired unused */ @Override public final void updateTriggers(Trigger lastFired) { } /** * Does nothing. * * @param player unused */ @Override public final void trigger(Player player) { } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/Rocker.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * A sign that has a deactivated and an activated state and can switch between these two. *

* For example, if a door sign is activated, the door opens - if it is deactivated, the door closes. The state may be set for the whole game world or for the * player who triggered the sign depending on the context. * * @author Daniel Saukel */ public abstract class Rocker extends Deactivatable { protected Rocker(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } /** * Activates the sign if it is not yet active and deactivates it if it is already active. * * @param player the player who triggered the sign or null if no one in particular triggered it */ @Override public void trigger(Player player) { if (!isActive()) { if (player != null) { activate(player); } else { activate(); } } else { deactivate(); } } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/sign/Windup.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.sign; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import org.bukkit.Bukkit; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; /** * A sign with an attached task that does actions in a set interval {@link #n} times, like a mob sign that spawns {@link #n} mobs. It is similar to a * {@link Rocker} as it expires (=is deactivated). * * @author Daniel Saukel */ public abstract class Windup extends Deactivatable { protected double delay = -1; protected double interval = -1; /** * How many times the task is supposed to be executed (unless it is cancelled). */ protected int n; private Runnable runnable; private BukkitTask task; protected Windup(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } /** * Returns the delay before the task runs in seconds. If no delay is specified, this uses the interval. * * @return the delay before the task runs in seconds. If no delay is specified, this uses the interval */ public double getDelaySeconds() { return delay != -1 ? delay : interval; } /** * Returns the delay before the task runs in ticks. If no delay is specified, this uses the interval. * * @return the delay before the task runs in ticks. If no delay is specified, this uses the interval */ public long getDelayTicks() { return delay != -1 ? (long) (delay * 20L) : getIntervalTicks(); } /** * Returns the task interval in seconds. * * @return the task interval in seconds */ public double getIntervalSeconds() { return interval; } /** * Returns the task interval in ticks. * * @return the task interval in ticks */ public long getIntervalTicks() { return (long) (interval * 20L); } /** * Returns the underlying task if it has started yet or null if not. * * @return the underlying task if it has started yet or null if not */ public BukkitTask getTask() { return task; } /** * Starts the runnable. */ public void startTask() { task = Bukkit.getScheduler().runTaskTimer(api, runnable, getDelayTicks(), getIntervalTicks()); } /** * Returns the runnable. * * @return the runnable */ public Runnable getRunnable() { return runnable; } /** * Sets the runnable. * * @param runnable the runnable */ public void setRunnable(Runnable runnable) { this.runnable = runnable; } /** * Returns how many times the task is supposed to be executed (like in SIGMA notation). * * @return how many times the task is supposed to be executed (like in SIGMA notation) */ public int getN() { return n; } /** * Sets how many times the task is supposed to be executed (like in SIGMA notation). * * @param n the new amount of runs */ public void setN(int n) { this.n = n; } @Override public void activate() { if (interval <= 0) { for (int k = 0; k < n; k++) { runnable.run(); } } else { active = true; startTask(); } } /** * Cancels the {@link #getTask() task}. */ @Override public void deactivate() { active = false; if (getTask() != null) { getTask().cancel(); } } /** * Activates the sign if it is not yet active and deactivates it if it is already active. * * @param player the player who triggered the sign or null if no one in particular triggered it */ @Override public void trigger(Player player) { if (!isActive()) { if (player != null) { activate(player); } else { activate(); } } } /** * Use this method to ensure that its world still exists. * * @return if the world is already finished */ public boolean isWorldFinished() { return Bukkit.getWorld(worldName) == null; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/trigger/AbstractTrigger.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.event.trigger.TriggerUnregistrationEvent; import de.erethon.dungeonsxl.api.world.GameWorld; import java.util.HashSet; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.entity.Player; /** * Skeletal implementation of {@link Trigger}. * * @author Daniel Saukel, Frank Baumann, Milan Albrecht */ public abstract class AbstractTrigger implements Trigger { private Set listeners = new HashSet<>(); private GameWorld gameWorld; private LogicalExpression expression; private String value; private Player player; protected AbstractTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { listeners.add(owner); gameWorld = owner.getGameWorld(); this.expression = expression; expression.setTrigger(this); this.value = value; } @Override public String getValue() { return value; } @Override public GameWorld getGameWorld() { return gameWorld; } @Override public boolean isTriggered() { return expression.isSatisfied(); } @Override public void setTriggered(boolean triggered) { expression.setSatisfied(triggered); } @Override public Player getTriggeringPlayer() { return player; } @Override public void setTriggeringPlayer(Player player) { this.player = player; } @Override public Set getListeners() { return listeners; } @Override public boolean addListener(TriggerListener owner) { return listeners.add(owner); } @Override public boolean removeListener(TriggerListener listener) { return listeners.remove(listener); } @Override public boolean unregisterTrigger() { TriggerUnregistrationEvent event = new TriggerUnregistrationEvent(this); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { return gameWorld.unregisterTrigger(this); } return false; } @Override public void updateListeners() { for (TriggerListener dSign : listeners.toArray(TriggerListener[]::new)) { dSign.updateTriggers(this); } } @Override public String toString() { return getClass().getSimpleName() + "{expression=" + expression + "}"; } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/trigger/LogicalExpression.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.GameWorld; import java.util.ArrayList; import java.util.List; /** * This class represents a logical expression of elements in an AND and OR relation to each other. *

* Valid operators are: *

    *
  • , = AND
  • *
  • / = OR
  • *
  • () = priority
  • *
* OR is prioritized over AND if no brackets indicate otherwise. *

* Logical expressions tolerate spaces and redundant operators, but no wrong brackets. * * @author Daniel Saukel */ public class LogicalExpression { private enum ComponentType { FIRST, AND, OR; @Override public String toString() { return this == FIRST ? "" : name() + ":"; } } /** * A satisfied, empty expression. */ public static final LogicalExpression EMPTY = new LogicalExpression(ComponentType.FIRST, ""); static { EMPTY.satisfied = true; } private ComponentType type; private String text; private Trigger trigger; private List contents; private boolean satisfied; private LogicalExpression(ComponentType type, String text) { this.type = type; this.text = text; contents = new ArrayList<>(); } /** * Interpretes the given string as a expression. * * @param string the string to parse * @throws IllegalArgumentException if the string is not a valid logical expression. * @return a expression that represents the given string; null if erroneous */ public static LogicalExpression parse(String string) { if (string == null || string.isBlank()) { return null; } string = string.replace(" ", ""); // Crop redundant end int i = string.length() - 1; while (string.charAt(i) == ',' || string.charAt(i) == '/') { i--; } string = string.substring(0, i + 1); return parse(ComponentType.FIRST, string); } private static LogicalExpression parse(ComponentType type, String string) { LogicalExpression root = new LogicalExpression(type, string); if (!string.contains(",") && !string.contains("/")) { return root; } int i = 0; int bracketStart = -1; int innerBrackets = 0; int currentCompStart = 0; boolean bracketLegal = true; ComponentType currentCompType = ComponentType.FIRST; while (string.length() > i) { char c = string.charAt(i); if (c == '(') { if (!bracketLegal) { throw new IllegalArgumentException("Bracket on illegal position at index " + i); } if (bracketStart == -1) { bracketStart = i + 1; // One after bracket start } else { innerBrackets++; } } else if (c == ')') { if (bracketStart == -1) { throw new IllegalArgumentException("Closed bracket before it was opened at index " + i); } innerBrackets--; if (innerBrackets == -1) { // Bracket closed LogicalExpression subexpression = parse(currentCompType, string.substring(bracketStart, i)); if (bracketStart == 1 && i == string.length() - 1) { // Bracket encapsulates the whole string and was redundant return subexpression; } root.contents.add(subexpression); innerBrackets = 0; bracketStart = -1; currentCompStart = i + 2; } bracketLegal = false; } else if (bracketStart == -1 && (c == ',' || c == '/')) { if (currentCompStart == i) { // Multiple commas / leading comma -> ignore currentCompStart++; } else { // Component had ended if (i > currentCompStart) { // Component had ended root.contents.add(parse(currentCompType, string.substring(currentCompStart, i))); } currentCompStart = i + 1; currentCompType = c == ',' ? ComponentType.AND : ComponentType.OR; } bracketLegal = true; } else { bracketLegal = false; } i++; } if (innerBrackets != 0 || bracketStart != -1) { throw new IllegalArgumentException("Bracket never closed"); } // Last component if (currentCompStart < string.length()) { LogicalExpression comp = parse(currentCompType, string.substring(currentCompStart, i)); if (comp != null) { root.contents.add(comp); } } return root; } /** * Returns the text this expression wraps. *

* This is not guaranteed to be equal to the string that had been passed to {@link #parse(java.lang.String)} as it may be stripped from redundant symbols. * * @return the text this expression wraps */ public String getText() { return text; } /** * Returns if this expression only consists of exactly one component. * * @return if this expression only consists of exactly one component */ public boolean isAtomic() { return contents.isEmpty(); } void setTrigger(Trigger trigger) { this.trigger = trigger; } /** * Creates a {@link Trigger} from an atomic element of the expression. * * @param api the {@link DungeonsAPI} reference; not null * @param listener the listener that belongs to the trigger; not null * @param generic if a generic trigger should be created if there is no specific trigger identifier (e.g. second line of a trigger sign) * @throws IllegalArgumentException if the listener is null or not in a {@link GameWorld} * @return the {@link Trigger} the string represents; null if there is none. */ public Trigger toTrigger(DungeonsAPI api, TriggerListener listener, boolean generic) { if (trigger != null) { return trigger; } if (!isAtomic()) { return null; } if (listener == null || listener.getGameWorld() == null) { throw new IllegalArgumentException("Listener must not be null and must be in a game world"); } listener.getGameWorld().createTrigger(listener, this); return trigger; } /** * Returns a List of the contents of this expression.

* Changes made to this list do not update the expression. * * @param deep if true, brackets are resolved to atomic components; if false, brackets are one element in the list * @return a List of the contents the contents of this expression */ public List getContents(boolean deep) { if (!deep || isAtomic()) { return new ArrayList<>(contents); } List atomicContents = new ArrayList<>(); for (LogicalExpression comp : contents) { if (comp.isAtomic()) { atomicContents.add(comp); } else { atomicContents.addAll(comp.getContents(true)); } } return atomicContents; } /** * Returns if this expression is satisfied. * * @return if this expression is satisfied */ public boolean isSatisfied() { if (isAtomic()) { return satisfied; } int[] orChains = new int[contents.size()]; int ors = 0; boolean inChain = false; boolean chainSatisfied = false; // Check OR chains, return false if NONE is satisfied for (int i = 1; contents.size() > i; i++) { if (contents.get(i).type != ComponentType.OR) { if (inChain) { if (!chainSatisfied) { return false; } orChains[ors] = i - 1; // write to even index inChain = false; chainSatisfied = false; ors++; // switch to even index for possible upcoming chain } continue; } if (!inChain) { inChain = true; orChains[ors] = i - 1; // write to even index chainSatisfied = contents.get(i - 1).isSatisfied(); ors++; // switch to uneven index } else { orChains[ors] = i; // update uneven index } if (contents.get(i).isSatisfied()) { chainSatisfied = true; } } if (inChain) { if (!chainSatisfied) { return false; } orChains[ors] = contents.size() - 1; } // Check AND chains, return false if ONE IS NOT satisfied int i = 0, j = 0; while (contents.size() > i) { if (!(orChains[j] == 0 && orChains[j + 1] == 0)) { while (i < orChains[j]) { if (!contents.get(i).isSatisfied()) { return false; } i++; } j++; i = orChains[j] + 1; j++; } else { if (!contents.get(i).isSatisfied()) { return false; } i++; } } return true; } /** * Sets the value of an atomic expression to the given boolean. *

* If the expression is not atomic, update the atomic components instead. * * @param satisfied if the expression is satisfied * @return if the expression could be updated (false if the expression is not atomic) */ public boolean setSatisfied(boolean satisfied) { if (!isAtomic()) { return false; } this.satisfied = satisfied; return true; } @Override public String toString() { if (contents.isEmpty()) { return type + text + (isSatisfied() ? "(t)" : "(f)"); } return type + contents.toString() + (isSatisfied() ? "(t)" : "(f)"); } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/trigger/Trigger.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.trigger; import com.google.common.collect.Sets; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.event.trigger.TriggerActionEvent; import static de.erethon.dungeonsxl.api.trigger.TriggerTypeKey.*; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.chat.MessageUtil; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.entity.Player; /** * A condition to fulfill in order to trigger a {@link TriggerListener} such as a {@link de.erethon.dungeonsxl.api.sign.DungeonSign}. *

* Implementations of this interface must always include a constructor of the types (DungeonsAPI, TriggerListener, LogicalExpression, String). * * @author Daniel Saukel */ public interface Trigger { /** * A Set of trigger types that are looked up in their game world before they are created, and, if an equal trigger already exists, returned instead of a new * one. For example, an instance trigger is not created when its entered into the trigger line of a listening sign, but when an interact sign explicitly * creates it. * * @see GameWorld#createTrigger(TriggerListener, LogicalExpression) */ static final Set IDENTIFIABLE = Sets.newHashSet(GENERIC, INTERACT, MOB, PROGRESS, USE_ITEM, WAVE); /** * Constructs a subtype of Trigger. *

* Implementations of this interface must always include a constructor of the types (DungeonsAPI, TriggerListener, LogicalExpression, String). * * @param the type of the trigger * @param typeKey the key that represents the type on dungeon signs and in the registry, see {@link TriggerTypeKey} * @param api the API instance; not null * @param owner an object that listens to the trigger * @param expression the atomic expression that the trigger uses; not null * @param value the value of the trigger. This is the text of the expression without the type key; not null * @see GameWorld#createTrigger(TriggerListener, LogicalExpression) * @see AbstractTrigger#AbstractTrigger(DungeonsAPI, TriggerListener, LogicalExpression, String) * @return the constructed Trigger object */ static T construct(char typeKey, DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { Class clss = null; try { clss = (Class) api.getTriggerRegistry().get(typeKey); return construct(clss, api, owner, expression, value); } catch (Exception exception) { MessageUtil.log(api, "&4It looks like the trigger \"" + typeKey + "\"/" + clss + " was not registered correctly:"); exception.printStackTrace(); return null; } } /** * Constructs a subtype of Trigger. *

* Implementations of this interface must always include a constructor of the types (DungeonsAPI, TriggerListener, LogicalExpression, String). * * @param the type of the trigger * @param clss the implementation Class object * @param api the API instance * @param owner an object that listens to the trigger * @param expression the atomic expression that the trigger uses; not null * @param value the value of the trigger. This is the text of the expression without the type key; not null * @see GameWorld#createTrigger(TriggerListener, LogicalExpression) * @see AbstractTrigger#AbstractTrigger(DungeonsAPI, TriggerListener, LogicalExpression, String) * @return the constructed Trigger object */ static T construct(Class clss, DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { try { Constructor constructor = clss.getConstructor(DungeonsAPI.class, TriggerListener.class, LogicalExpression.class, String.class); return (T) constructor.newInstance(api, owner, expression, value); } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { MessageUtil.log(api, "&4Could not create a trigger of the type \"" + clss + "\". A trigger implementation needs a constructor with the types (DungeonsAPI, TriggerListener, LogicalExpression, String)."); exception.printStackTrace(); return null; } } /** * Returns a char that identifies the trigger as one of a specific type. *

* Triggers from the default implementations are stored in {@link TriggerTypeKey}. * * @return a char that identifies the trigger as one of a specific type */ char getKey(); /** * Returns the raw value the trigger was initialized with. May contain an identifier or arguments in any shape or form. * * @return the raw value the trigger was initialized with. May contain an identifier or arguments in any shape or form */ String getValue(); /** * The {@link GameWorld} the trigger works in. * * @return the {@link GameWorld} the trigger works in */ GameWorld getGameWorld(); /** * Returns if the trigger is triggered. * * @return if the trigger is triggered */ boolean isTriggered(); /** * Sets if the trigger is triggered. * * @param triggered the state of the trigger */ void setTriggered(boolean triggered); /** * Returns the last player who triggered the trigger. * * @return the last player who triggered the trigger */ Player getTriggeringPlayer(); /** * Updates {@link #getTriggeringPlayer()} to the given player. * * @param player the player to set */ void setTriggeringPlayer(Player player); /** * A set of the objects that listen to this trigger. * * @return a set of the objects that listen to this trigger */ Set getListeners(); /** * Adds the given object to listen to this trigger. * * @param listener the listener to add * @return if adding the listener was successful */ boolean addListener(TriggerListener listener); /** * Removes the given listener. * * @param listener the listener to remove * @return if removing the listener was successful */ boolean removeListener(TriggerListener listener); /** * Unregisters the trigger from the {@link GameWorld}. *

* This is used to disable triggers, such as distance triggers removing themselves after the first player got into range. * * @return if unregistering the listener was successful */ boolean unregisterTrigger(); /** * Updates the listeners; to be used when the state of the trigger is updated; by default in {@link #trigger(boolean, Player)}. */ void updateListeners(); /** * Called when the trigger is triggered. *

* This method provides default procedures when the conditions of any trigger are fulfilled, such as calling events and updating listeners. Use * {@link #onTrigger(boolean)} to implement specific behavior (such as self-removal for default distance triggers). *

* This method does NOT change {@link #isTriggered()}. Due to the difference in behavior of triggers (working button-like, switch-like etc.), this is to be * handled in {@link #onTrigger(boolean)}. * * @param switching if the action changes the the state of {@link #isTriggered()} * @param triggeringPlayer the player who fulfilled the (last) conditions of this trigger */ default void trigger(boolean switching, Player triggeringPlayer) { List fired = getListeners().stream() .filter(l -> l.getTriggerExpression().isSatisfied()) .toList(); TriggerActionEvent event = new TriggerActionEvent(this, fired); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } setTriggeringPlayer(triggeringPlayer); onTrigger(switching); updateListeners(); postTrigger(); } /** * Called when the trigger is triggered. *

* This method can be used to implement specific behavior (such as self-removal for default distance triggers). Call {@link #trigger(boolean, Player)} * instead when the condition to trigger the trigger is fulfilled. * * @param switching if the action changes the the state of {@link #isTriggered()} */ void onTrigger(boolean switching); /** * Called after listeners are updated. *

* This method can be used to implement specific behavior such as reactivating itself after the actions of the listeners are done, such as presence triggers * performing their action every time a player gets close. */ default void postTrigger() { } } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/trigger/TriggerListener.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.trigger; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.world.GameWorld; import java.util.List; import org.bukkit.Location; import org.bukkit.entity.Player; /** * Represents things that wait until a trigger is fired and then react to it, such as {@link de.erethon.dungeonsxl.api.sign.DungeonSign}. * * @author Daniel Saukel */ public interface TriggerListener { /** * Returns a {@link LogicalExpression} of the triggers registered for this listener. * * @return a a {@link LogicalExpression} of the triggers registered for this listener */ LogicalExpression getTriggerExpression(); /** * Returns a deep List of triggers of this listener. *

* WARNING: Do not iterate and check if each is satisfied to get if the sign should fire. Use {@link #getTriggerExpression()} instead. * * @return a deep List of triggers of this listener */ List getTriggers(); /** * Returns if the listener has triggers. * * @return if the listener has triggers */ default boolean hasTriggers() { return getTriggerExpression() != null; } /** * The location of this listener. * * @return the location of this listener */ Location getLocation(); /** * Returns the game world this listener is in; null if this is in an edit world. * * @return the game world this listener is in; null if this is in an edit world */ GameWorld getGameWorld(); /** * Returns the game played in the world of this listener. * * @return the game played in the world of this listener */ default Game getGame() { if (getGameWorld() == null) { return null; } return getGameWorld().getGame(); } /** * Makes the listener listen for its triggers if it {@link #hasTriggers()}. *

* {@link #trigger(org.bukkit.entity.Player)}s the listener if it does not have any triggers. (Note that some signs have interaction triggers by default, * like ready signs). */ void initialize(); /** * Returns if the listener is {@link #initialize()}d. * * @return if the listener is {@link #initialize()}d */ boolean isInitialized(); /** * Triggers the listener. The effects are defined by the implementation. * * @param player the player who triggered the listener or null if no one in particular triggered it */ void trigger(Player player); /** * Checks if the triggers of the listener have been triggered. If they all are, the listener itself is triggered. * * @param lastFired the last trigger that has been triggered */ void updateTriggers(Trigger lastFired); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/trigger/TriggerTypeKey.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.trigger; /** * Type keys of default triggers. * * @author Daniel Saukel */ public class TriggerTypeKey { public static final char DISTANCE = 'D'; public static final char FORTUNE = 'F'; public static final char INTERACT = 'I'; /** * The terms "generic" and "sign trigger" are used synonymously. Trigger strings without prefix default to generic triggers. */ public static final char GENERIC = 'T'; public static final char MOB = 'M'; public static final char PRESENCE = 'P'; @Deprecated public static final char PROGRESS = 'P'; public static final char REDSTONE = 'R'; public static final char USE_ITEM = 'U'; @Deprecated public static final char WAVE = 'W'; } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/world/EditWorld.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.world; import org.bukkit.block.Block; /** * A raw resource world instance to edit the dungeon map. There is never more than one edit world per resource world. *

* An edit world is not equal to a {@link de.erethon.dungeonsxl.api.dungeon.Dungeon}. * * @author Daniel Saukel */ public interface EditWorld extends InstanceWorld { /** * Registers the block as a {@link de.erethon.dungeonsxl.api.sign.DungeonSign} sothat it can later be saved persistently. * * @param block a DungeonSign block */ void registerSign(Block block); /** * Saves the sign data and overrides the resource with the changes. */ void save(); @Override default void delete() { delete(true); } /** * Deletes this edit instance. * * @param save whether this world should be {@link #save()}ed */ void delete(boolean save); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/world/GameWorld.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.world; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.mob.DungeonMob; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import java.util.Collection; import java.util.List; import org.bukkit.Location; import org.bukkit.block.Block; /** * A playable resource instance. There may be any amount of GameWorlds per {@link ResourceWorld}. *

* A game world is not equal to a {@link de.erethon.dungeonsxl.api.dungeon.Dungeon}. * * @author Daniel Saukel */ // Implementation-specific methods: [gameblock, secure objects, classes signs, mobs, triggers] methods, getMobCount(), setPlaying(), startGame(), listener methods public interface GameWorld extends InstanceWorld { enum Type { START_FLOOR, END_FLOOR, DEFAULT } /** * Returns the {@link Type} of this GameWorld. * * @return the {@link Type} of this GameWorld */ Type getType(); /** * Sets the {@link Type} of this GameWorld. * * @param type the type */ void setType(Type type); /** * Returns the game that is played in the game world. * * @return the game that is played in the game world */ Game getGame(); /** * Returns the dungeon that the game world is part of. *

* Note: While a {@link ResourceWorld} may be part of multiple dungeons, an instance is instantiated per game and thus has just one dungeon. * * @return the dungeon that the game world is part of */ Dungeon getDungeon(); /** * Creates a trigger represented by the given atomic expression. *

* For example, if the expression may wrap the string "D 10", but not "D 10, M ZOMBIE". *

* Use {@link #createTriggers(TriggerListener, LogicalExpression)} to get an array of all {@link Trigger} objects for a compound expression (such as just "D * 10, M ZOMBIE"). * * @param owner the {@link TriggerListener} that the trigger belongs to * @param expression the expression; must be {@link LogicalExpression#isAtomic() atomic}. * @throws IllegalArgumentException if the expression is not atomic * @throws IllegalStateException if the owner is not in a game world * @return a trigger represented by the given atomic expression; null if the expression is {@link LogicalExpression#EMPTY}. */ Trigger createTrigger(TriggerListener owner, LogicalExpression expression); /** * Creates triggers represented by the given expression. *

* For example, if the expression wraps the string "D 10, M ZOMBIE", this returns an array where the 0st entry represents "D 10" and the 1st "M ZOMBIE". *

* Use {@link #createTrigger(TriggerListener, LogicalExpression)} to get one {@link Trigger} object for exactly one atomic part (such as just "D 10"). * * @param owner the {@link TriggerListener} that the trigger belongs to * @param expression the expression; must be {@link LogicalExpression#isAtomic() atomic}. * @throws IllegalStateException if the owner is not in a game world * @return a List of triggers represented by the given expression; an empty List if the expression is {@link LogicalExpression#EMPTY}. */ List createTriggers(TriggerListener owner, LogicalExpression expression); /** * Returns a Collection of the triggers registered in this world. * * @return a Collection of the triggers registered in this world */ Collection getTriggers(); /** * Returns a Collection of the triggers registered in this world that use the given key. * * @param key the key char * @see de.erethon.dungeonsxl.api.trigger.TriggerTypeKey * @return a Collection of the triggers registered in this world that use the given key. */ Collection getTriggersFromKey(char key); /** * Unregisters the given trigger, which prevents them from firing unless explicitly done in code. * * @param trigger the trigger to unregister * @return if unregistering the trigger was successful */ boolean unregisterTrigger(Trigger trigger); /** * Returns the living dungeon mobs. * * @return the living dungeon mobs */ Collection getMobs(); /** * Registers the given dungeon mob. * * @param mob the mob */ void addMob(DungeonMob mob); /** * Unregisters the given dungeon mob. * * @param mob the mob */ public void removeMob(DungeonMob mob); /** * Returns if the game has begun in the game world. * * @return if the game has begun in the game world */ boolean isPlaying(); /** * Returns the start location of the world. This may be set by a start {@link de.erethon.dungeonsxl.api.sign.DungeonSign sign} or, if none exists, the * Vanilla spawn location of the {@link #getWorld() world}. * * @param group each group might have its own start location * @return the start location of the world */ Location getStartLocation(PlayerGroup group); /** * Returns if it is required to choose a class in order to start the game. * * @return if it is required to choose a class in order to start the game */ boolean areClassesEnabled(); /** * Sets if it is required to choose a class in order to start the game. * * @param enabled if it is required to choose a class in order to start the game */ void setClassesEnabled(boolean enabled); /** * Returns a collection of the blocks that have been placed by players in the current game. * * @return a collection of the blocks that have been placed by players in the current game */ Collection getPlacedBlocks(); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/world/InstanceWorld.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.world; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.sign.DungeonSign; import java.io.File; import java.util.Collection; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Sign; /** * Super interface for worlds that are instantiated by DungeonsXL. *

* An instance world is not equal to a {@link de.erethon.dungeonsxl.api.dungeon.Dungeon}. * * @author Daniel Saukel */ // Implementation-specific methods: getConfig, exists, setWeather public interface InstanceWorld { /** * Returns the name of the resource world of this instance. *

* Use {@link #getWorld()}{@link org.bukkit.World#getName() #getName()} to get the name of the instantiated world (like e.g. DXL_Game_1). * * @return the name of the resource world of this instance */ String getName(); /** * Returns the saved map this instance was loaded from. * * @return the saved map this instance was loaded from */ ResourceWorld getResource(); /** * Returns the world folder. * * @return the world folder */ File getFolder(); /** * Returns the wrapped Bukkit world. * * @return the wrapped Bukkit world */ World getWorld(); /** * Returns the ID. This is usually the number in the map name. * * @return the ID */ int getId(); /** * Returns a collection of the signs in this instance. * * @return a collection of the signs in this instance */ Collection getDungeonSigns(); /** * Creates a dungeon sign in this instance. * * @param sign the sign block * @param lines the lines of the sign * @return the created sign */ DungeonSign createDungeonSign(Sign sign, String[] lines); /** * Removes the given dungeon sign from this instance. * * @param sign the sign */ void removeDungeonSign(DungeonSign sign); /** * Removes the dungeon sign represented by the given sign block from this instance. * * @param sign the sign block */ void removeDungeonSign(Block sign); /** * Returns the DungeonSign represented by the given sign block. * * @param sign the sign block * @return the DungeonSign represented by the given sign block */ DungeonSign getDungeonSign(Block sign); /** * Returns the location of the lobby where players spawn by default when they are teleported into the dungeon. * * @return the location of the lobby where players spawn by default when they are teleported into the dungeon */ Location getLobbyLocation(); /** * Sets the default spawn location of the instance. *

* This is not persistent and does not create a lobby sign. * * @param location the location */ void setLobbyLocation(Location location); /** * Returns the players in the instance. * * @return the players in the instance */ Collection getPlayers(); /** * Sends a message to all players in the instance. * * @param message the message to send */ void sendMessage(String message); /** * Makes all players leave the world. Attempts to let them leave properly if they are correct DInstancePlayers; teleports them to the spawn if they are not. */ void kickAllPlayers(); /** * Deletes this instance. */ void delete(); } ================================================ FILE: api/src/main/java/de/erethon/dungeonsxl/api/world/ResourceWorld.java ================================================ /* * Copyright (C) 2015-2026 Daniel Saukel * * This library is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNULesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with * this program. If not, see . */ package de.erethon.dungeonsxl.api.world; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import java.io.File; import org.bukkit.OfflinePlayer; import org.bukkit.World.Environment; /** * A stored world that can be instantiated as an {@link EditWorld} or as a {@link GameWorld}. *

* In the default implementation, these are saved under "plugins/DungeonsXL/maps/". *

* A resource world is not equal to a {@link de.erethon.dungeonsxl.api.dungeon.Dungeon}. * * @author Daniel Saukel */ // Implementation-specific methods: getSignData, generate public interface ResourceWorld { /** * Returns the name of this resource world. *

* Equals {@link #getFolder()}{@link File#getName() #getName()}. * * @return name of this resource world */ String getName(); /** * Renames the resource world and its folder. * * @param name the new name */ void setName(String name); /** * Returns the folder where this resource is stored. * * @return the folder where this resource is stored */ File getFolder(); /** * Returns the {@link de.erethon.dungeonsxl.api.dungeon.GameRule}s of this world. *

* Note that these are only the rules that are specific to the map itself. They are not the rules that are actually used in a game instance instantiated * from this resource world as these ones may be supplemented or overriden by other rules taken from the main config, dungeon config or the * {@link de.erethon.dungeonsxl.api.dungeon.GameRule#DEFAULT_VALUES}. * * @return the {@link de.erethon.dungeonsxl.api.dungeon.GameRule}s of this world */ GameRuleContainer getRules(); /** * Returns the environment of the world as defined in the config or {@link org.bukkit.World.Environment#NORMAL} if nothing is set. * * @return the environment of the world as defined in the config or {@link org.bukkit.World.Environment#NORMAL} if nothing is set */ Environment getWorldEnvironment(); /** * Adds the player to the list of players that are invited to edit the resource. * * @param player the player */ void addInvitedPlayer(OfflinePlayer player); /** * Removes a player from the list of players that are invited to edit the resource. * * @param player the player * @return if the action was successful */ boolean removeInvitedPlayer(OfflinePlayer player); /** * Returns if the player is invited to edit the resource. * * @param player the player * @return if the player is invited to edit the resource */ boolean isInvitedPlayer(OfflinePlayer player); /** * Creates a backup of the resource. */ void backup(); /** * Returns the loaded edit instance of this world or null if none exists. * * @return the loaded edit instance of this world or null if none exists */ EditWorld getEditWorld(); /** * Returns the loaded edit instance of this world or generates a new one if none exists. * * @param ignoreLimit if the instance limit set in the main config shall be ignored * @return the loaded edit instance of this world or generates a new one if none exists */ EditWorld getOrInstantiateEditWorld(boolean ignoreLimit); /** * Returns a new game instance of this resource. * * @see de.erethon.dungeonsxl.api.dungeon.Game#ensureWorldIsLoaded(boolean) * @param game the game the instance belongs to * @param ignoreLimit if the instance limit set in the main config shall be ignored * @return a new game instance of this resource */ GameWorld instantiateGameWorld(Game game, boolean ignoreLimit); /** * Returns the single floor dungeon of this resource. * * @return the single floor dungeon of this resource */ Dungeon getSingleFloorDungeon(); } ================================================ FILE: build.bat ================================================ java -jar mvnbt.jar pause ================================================ FILE: build.sh ================================================ #!/bin/bash java -jar mvnbt.jar read -rp "Press any key to continue..." -n 1 ================================================ FILE: bukkit_blockdata/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-bukkit_blockdata 0.19-SNAPSHOT jar de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT de.erethon.dungeonsxl dungeonsxl-adapter ${project.parent.version} compile de.erethon.dungeonsxl dungeonsxl-api ${project.parent.version} compile org.spigotmc spigot-api ${spigotVersion.latest} provided ================================================ FILE: bukkit_blockdata/src/main/java/de/erethon/dungeonsxl/adapter/block/BlockAdapterBlockData.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.adapter.block; import de.erethon.dungeonsxl.api.player.PlayerGroup.Color; import org.bukkit.Axis; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.block.data.Openable; import org.bukkit.block.data.Orientable; import org.bukkit.block.data.Rotatable; import org.bukkit.block.data.type.Bed; /** * @author Daniel Saukel */ public class BlockAdapterBlockData implements BlockAdapter { @Override public boolean isBedHead(Block block) { if (!(block.getBlockData() instanceof Bed)) { throw new IllegalArgumentException("Block is not Bed"); } return ((Bed) block.getBlockData()).getPart() == Bed.Part.HEAD; } @Override public void openDoor(Block block) { if (!(block.getBlockData() instanceof Openable)) { throw new IllegalArgumentException("Block is not Openable"); } Openable data = (Openable) block.getBlockData(); data.setOpen(true); block.setBlockData(data); } @Override public void closeDoor(Block block) { if (!(block.getBlockData() instanceof Openable)) { throw new IllegalArgumentException("Block is not Openable"); } Openable data = (Openable) block.getBlockData(); data.setOpen(false); block.setBlockData(data); } @Override public void setBlockWoolColor(Block block, Color color) { block.setType(color.getWoolMaterial().getMaterial()); } @Override public BlockFace getFacing(Block block) { if (block.getBlockData() instanceof Directional) { return ((Directional) block.getBlockData()).getFacing(); } else if (block.getBlockData() instanceof Rotatable) { return ((Rotatable) block.getBlockData()).getRotation(); } else { throw new IllegalArgumentException("Block is not Directional or Rotatable"); } } @Override public void setFacing(Block block, BlockFace facing) { BlockData data = block.getBlockData(); if (data instanceof Directional) { ((Directional) data).setFacing(facing); } else if (data instanceof Rotatable) { ((Rotatable) data).setRotation(facing); } else { throw new IllegalArgumentException("Block is not Directional or Rotatable"); } block.setBlockData(data, false); } @Override public void setAxis(Block block, boolean z) { if (!(block.getBlockData() instanceof Orientable)) { throw new IllegalArgumentException("Block is not Orientable"); } Orientable data = (Orientable) block.getBlockData(); data.setAxis(z ? Axis.Z : Axis.X); block.setBlockData(data, false); } } ================================================ FILE: bukkit_magicvalues/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-bukkit_magicvalues 0.19-SNAPSHOT jar de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT de.erethon.dungeonsxl dungeonsxl-adapter ${project.parent.version} compile de.erethon.dungeonsxl dungeonsxl-api ${project.parent.version} compile org.spigotmc spigot-api 1.12.2-R0.1-SNAPSHOT provided ================================================ FILE: bukkit_magicvalues/src/main/java/de/erethon/dungeonsxl/adapter/block/BlockAdapterMagicValues.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.adapter.block; import de.erethon.dungeonsxl.api.player.PlayerGroup.Color; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.material.Bed; import org.bukkit.material.Directional; import org.bukkit.material.MaterialData; /** * @author Daniel Saukel */ public class BlockAdapterMagicValues implements BlockAdapter { @Override public boolean isBedHead(Block block) { MaterialData data = block.getState().getData(); if (!(data instanceof Bed)) { throw new IllegalArgumentException("Block is not Bed"); } return ((Bed) data).isHeadOfBed(); } @Override public void openDoor(Block block) { block.setData((byte) (block.getData() + 4)); } @Override public void closeDoor(Block block) { block.setData((byte) (block.getData() - 4)); } @Override public void setBlockWoolColor(Block block, Color color) { block.setTypeIdAndData(Material.WOOL.getId(), color.getDyeColor().getWoolData(), false); } @Override public BlockFace getFacing(Block block) { MaterialData data = block.getState().getData(); if (!(data instanceof Directional)) { throw new IllegalArgumentException("Block is not Directional"); } return ((Directional) data).getFacing(); } @Override public void setFacing(Block block, BlockFace facing) { BlockState state = block.getState(); MaterialData data = state.getData(); if (!(data instanceof Directional)) { throw new IllegalArgumentException("Block is not Directional"); } ((Directional) data).setFacingDirection(facing); state.setData(data); state.update(); } @Override public void setAxis(Block block, boolean z) { block.setData(z ? (byte) 2 : 1); } } ================================================ FILE: core/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-core 0.19-SNAPSHOT jar de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT 2.7.5 2.0.26-SNAPSHOT 3.0.4 3.2.16 2.12.2 . true src/main/resources/ plugin.yml languages/* org.codehaus.mojo properties-maven-plugin 1.3.0 generate-resources write-project-properties ${project.build.outputDirectory}/dxl.properties de.erethon.dungeonsxl dungeonsxl-adapter ${project.parent.version} compile de.erethon.dungeonsxl dungeonsxl-api ${project.parent.version} compile de.erethon.dungeonsxl dungeonsxl-bukkit_blockdata ${project.parent.version} compile de.erethon.dungeonsxl dungeonsxl-bukkit_magicvalues ${project.parent.version} compile org.spigotmc spigot-api ${spigotVersion.latest} provided com.github.MilkBowl VaultAPI 1.7 provided org.bukkit bukkit net.citizensnpcs citizens-main ${dependencyVersion.citizens} provided me.filoghost.holographicdisplays holographicdisplays-legacy-api-v2 ${dependencyVersion.holographicdisplays} provided org.black_ixx BossShop ${dependencyVersion.bossshop} provided com.griefcraft Modern-LWC 2.1.2 provided me.clip placeholderapi ${dependencyVersion.placeholderapi} provided com.alessiodp.parties parties-api ${dependencyVersion.parties} provided jitpack.io https://jitpack.io citizens-repo https://repo.citizensnpcs.co/ placeholderapi https://repo.extendedclip.com/content/repositories/placeholderapi/ codemc-repo https://repo.codemc.org/repository/maven-public/ alessiodp-repo https://repo.alessiodp.com/releases/ ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/DXLModule.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl; import de.erethon.dungeonsxl.api.DungeonModule; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.Trigger; import static de.erethon.dungeonsxl.api.trigger.TriggerTypeKey.*; import de.erethon.dungeonsxl.requirement.*; import de.erethon.dungeonsxl.reward.*; import de.erethon.dungeonsxl.sign.button.*; import de.erethon.dungeonsxl.sign.passive.*; import de.erethon.dungeonsxl.sign.rocker.*; import de.erethon.dungeonsxl.sign.windup.*; import de.erethon.dungeonsxl.trigger.*; import de.erethon.xlib.util.Registry; /** * @author Daniel Saukel */ public class DXLModule implements DungeonModule { @Override public void initRequirements(Registry> requirementRegistry) { requirementRegistry.add("feeLevel", FeeLevelRequirement.class); requirementRegistry.add("feeMoney", FeeMoneyRequirement.class); requirementRegistry.add("finishedDungeons", FinishedDungeonsRequirement.class); requirementRegistry.add("forbiddenItems", ForbiddenItemsRequirement.class); requirementRegistry.add("groupSize", GroupSizeRequirement.class); requirementRegistry.add("keyItems", KeyItemsRequirement.class); requirementRegistry.add("permission", PermissionRequirement.class); requirementRegistry.add("timeSinceFinish", TimeSinceFinishRequirement.class); requirementRegistry.add("timeSinceStart", TimeSinceStartRequirement.class); requirementRegistry.add("timeframe", TimeframeRequirement.class); } @Override public void initRewards(Registry> rewardRegistry) { rewardRegistry.add("item", ItemReward.class); rewardRegistry.add("money", MoneyReward.class); rewardRegistry.add("level", LevelReward.class); } @Override public void initSigns(Registry> signRegistry) { signRegistry.add("ACTIONBAR", ActionBarSign.class); signRegistry.add("BED", BedSign.class); signRegistry.add("BLOCK", BlockSign.class); signRegistry.add("BOSSSHOP", BossShopSign.class); signRegistry.add("CHECKPOINT", CheckpointSign.class); signRegistry.add("CLASSES", ClassesSign.class); signRegistry.add("CMD", CommandSign.class); signRegistry.add("DROP", DropSign.class); signRegistry.add("DUNGEONCHEST", DungeonChestSign.class); signRegistry.add("END", EndSign.class); signRegistry.add("FLAG", FlagSign.class); signRegistry.add("HOLOGRAM", HologramSign.class); signRegistry.add("INTERACT", InteractSign.class); signRegistry.add("LEAVE", LeaveSign.class); signRegistry.add("LIVES", LivesModifierSign.class); signRegistry.add("LOBBY", LobbySign.class); signRegistry.add("MOB", MobSign.class); signRegistry.add("MSG", ChatMessageSign.class); signRegistry.add("NOTE", NoteSign.class); signRegistry.add("DOOR", OpenDoorSign.class); signRegistry.add("PLACE", PlaceSign.class); signRegistry.add("PROTECTION", ProtectionSign.class); signRegistry.add("READY", ReadySign.class); signRegistry.add("REDSTONE", RedstoneSign.class); signRegistry.add("RESOURCEPACK", ResourcePackSign.class); signRegistry.add("REWARDCHEST", RewardChestSign.class); signRegistry.add("SCRIPT", ScriptSign.class); signRegistry.add("SOUNDMSG", SoundMessageSign.class); signRegistry.add("START", StartSign.class); signRegistry.add("TELEPORT", TeleportSign.class); signRegistry.add("TITLE", TitleSign.class); signRegistry.add("TRIGGER", TriggerSign.class); signRegistry.add("WAVE", WaveSign.class); } @Override public void initGameRules(Registry gameRuleRegistry) { for (GameRule rule : GameRule.VALUES) { gameRuleRegistry.add(rule.getKey(), rule); } } @Override public void initTriggers(Registry> triggerRegistry) { triggerRegistry.add(DISTANCE, DistanceTrigger.class); triggerRegistry.add(FORTUNE, FortuneTrigger.class); triggerRegistry.add(INTERACT, InteractTrigger.class); triggerRegistry.add(MOB, MobTrigger.class); triggerRegistry.add(PRESENCE, PresenceTrigger.class); //triggerRegistry.add("P", ProgressTrigger.class); triggerRegistry.add(REDSTONE, RedstoneTrigger.class); triggerRegistry.add(GENERIC, SignTrigger.class); triggerRegistry.add(USE_ITEM, UseItemTrigger.class); triggerRegistry.add(WAVE, WaveTrigger.class); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/DungeonsXL.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl; import de.erethon.dungeonsxl.adapter.block.BlockAdapter; import de.erethon.dungeonsxl.adapter.block.BlockAdapterBlockData; import de.erethon.dungeonsxl.adapter.block.BlockAdapterMagicValues; import de.erethon.dungeonsxl.api.DungeonModule; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent; import de.erethon.dungeonsxl.api.mob.DungeonMob; import de.erethon.dungeonsxl.api.mob.ExternalMobProvider; import de.erethon.dungeonsxl.api.player.GroupAdapter; import de.erethon.dungeonsxl.api.player.PlayerCache; import de.erethon.dungeonsxl.api.player.PlayerClass; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.command.DCommandRegistry; import de.erethon.dungeonsxl.config.MainConfig; import de.erethon.dungeonsxl.config.MainConfig.BackupMode; import de.erethon.dungeonsxl.dungeon.DDungeon; import de.erethon.dungeonsxl.global.GlobalProtectionCache; import de.erethon.dungeonsxl.global.GlobalProtectionListener; import de.erethon.dungeonsxl.mob.CitizensMobProvider; import de.erethon.dungeonsxl.mob.CustomExternalMobProvider; import de.erethon.dungeonsxl.mob.DMob; import de.erethon.dungeonsxl.mob.DMobListener; import de.erethon.dungeonsxl.mob.ExternalMobPlugin; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DInstancePlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.player.DPlayerListener; import de.erethon.dungeonsxl.player.SecureModeTask; import de.erethon.dungeonsxl.player.groupadapter.*; import de.erethon.dungeonsxl.reward.RewardListener; import de.erethon.dungeonsxl.sign.DSignListener; import de.erethon.dungeonsxl.sign.button.EndSign; import de.erethon.dungeonsxl.sign.passive.RewardChestSign; import de.erethon.dungeonsxl.sign.passive.SignScript; import de.erethon.dungeonsxl.sign.windup.CommandScript; import de.erethon.dungeonsxl.sign.windup.MobSign; import de.erethon.dungeonsxl.trigger.TriggerListener; import de.erethon.dungeonsxl.util.DependencyVersion; import de.erethon.dungeonsxl.util.LWCUtil; import de.erethon.dungeonsxl.util.PlaceholderUtil; import de.erethon.dungeonsxl.world.DEditWorld; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.dungeonsxl.world.DWorldListener; import de.erethon.dungeonsxl.world.LWCIntegration; import de.erethon.dungeonsxl.world.WorldConfig; import de.erethon.xlib.XLib; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.mob.ExMob; import de.erethon.xlib.plugin.PluginInit; import de.erethon.xlib.util.FileUtil; import de.erethon.xlib.util.Registry; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import org.bukkit.Bukkit; import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataType; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Tobias Schmitz, Daniel Saukel */ public class DungeonsXL extends JavaPlugin implements DungeonsAPI { /* Plugin & lib instances */ private static DungeonsXL instance; private XLib xlib; private PluginInit init; /* Util instances */ public static final BlockAdapter BLOCK_ADAPTER = Version.isAtLeast(Version.MC1_13) ? new BlockAdapterBlockData() : new BlockAdapterMagicValues(); /* Constants */ public static final String[] EXCLUDED_FILES = {"config.yml", "uid.dat", "DXLData.data", "data"}; /* Folders of internal features */ public static final File SIGNS = new File(SCRIPTS, "signs"); public static final File COMMANDS = new File(SCRIPTS, "commands"); /* Legacy */ public static final Map> LEGACY_SIGNS = new HashMap<>(); static { LEGACY_SIGNS.put("CHEST", RewardChestSign.class); LEGACY_SIGNS.put("EXTERNALMOB", MobSign.class); LEGACY_SIGNS.put("FLOOR", EndSign.class); } /* Caches & registries */ private Set modules = new HashSet<>(); private Collection groupAdapters = new HashSet<>(); private PlayerCache playerCache; private Collection gameCache; private Registry classRegistry; private Registry> signRegistry; private Registry> requirementRegistry; private Registry> rewardRegistry; private Registry dungeonRegistry; private Registry mapRegistry; private Registry instanceCache; private Registry gameRuleRegistry; private Registry> triggerRegistry; private Registry externalMobProviderRegistry; private Registry playerGroupCache; @Deprecated private class SignRegistry extends Registry> { @Override public Class get(String key) { Class clss = super.get(key); if (clss == null) { return LEGACY_SIGNS.get(key); } return clss; } } private class GameRuleRegistry extends Registry { @Override public void add(String key, GameRule rule) { super.add(key, rule); if (loaded) { GameRule.DEFAULT_VALUES.setState(rule, rule.getDefaultValue()); mainConfig.getDefaultWorldConfig().updateGameRule(rule); for (Dungeon apiDungeon : dungeonRegistry) { DDungeon dungeon = ((DDungeon) apiDungeon); if (dungeon.isMultiFloor()) { dungeon.getConfig().getDefaultValues().updateGameRule(rule); dungeon.getConfig().getOverrideValues().updateGameRule(rule); } else { WorldConfig cfg = ((DResourceWorld) dungeon.getMap()).getConfig(false); cfg.updateGameRule(rule); } } dungeonRegistry.forEach(Dungeon::setupRules); } } } private class PlayerGroupCache extends Registry { @Override public PlayerGroup get(String key) { PlayerGroup group = elements.get(key); if (group != null) { return group; } for (PlayerGroup value : elements.values()) { if (((DGroup) value).getUntaggedName().equalsIgnoreCase(key)) { return value; } } return null; } } /* Global state variables */ private boolean loaded, loadingWorld; private MainConfig mainConfig; /* Caches & registries of internal features */ private GlobalProtectionCache protections; private Registry signScriptRegistry; private Registry commandScriptRegistry; @Override public void onEnable() { if (!DependencyVersion.XLIB.check()) { getLogger().log(Level.SEVERE, "DungeonsXL requires ItemsXL v{0} or higher to run.", DependencyVersion.XLIB); getServer().getPluginManager().disablePlugin(this); return; } instance = this; xlib = XLib.getInstance(); init = new PluginInit(this, xlib, DependencyVersion.META); initFolders(); DPermission.register(); registerModule(new DXLModule()); init(); checkState(); if (getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { new PlaceholderUtil(this, "dxl").register(); } if (getServer().getPluginManager().isPluginEnabled("Parties")) { registerGroupAdapter(new PartiesAdapter(this)); } init.init(new DCommandRegistry(this, init), mainConfig.isUpdaterEnabled()); loaded = true; } @Override public void onDisable() { if (!loaded) { return; } loaded = false; saveData(); deleteAllInstances(); HandlerList.unregisterAll(this); getServer().getScheduler().cancelTasks(this); DPermission.unregister(); } public void initFolders() { if (!getDataFolder().exists()) { getDataFolder().mkdir(); } BACKUPS.mkdir(); MAPS.mkdir(); PLAYERS.mkdir(); SCRIPTS.mkdir(); CLASSES.mkdir(); DUNGEONS.mkdir(); SIGNS.mkdir(); COMMANDS.mkdir(); } public void reload() { /* Add default values */ requirementRegistry = new Registry<>(); modules.forEach(m -> m.initRequirements(requirementRegistry)); rewardRegistry = new Registry<>(); modules.forEach(m -> m.initRewards(rewardRegistry)); signRegistry = new SignRegistry(); modules.forEach(m -> m.initSigns(signRegistry)); gameRuleRegistry = new GameRuleRegistry(); modules.forEach(m -> m.initGameRules(gameRuleRegistry)); triggerRegistry = new Registry<>(); modules.forEach(m -> m.initTriggers(triggerRegistry)); mainConfig = new MainConfig(this, new File(getDataFolder(), "config.yml")); /* Maps & dungeons */ // Maps mapRegistry = new Registry<>(); for (File file : MAPS.listFiles()) { if (file.isDirectory() && !file.getName().equals(".raw")) { mapRegistry.add(file.getName(), new DResourceWorld(this, file)); } } // Dungeons - Map dungeons dungeonRegistry = new Registry<>(); for (ResourceWorld resource : mapRegistry) { dungeonRegistry.add(resource.getName(), new DDungeon(this, resource)); } // Dungeons - Linked dungeons if (init.isXLDevMode()) { for (File file : DUNGEONS.listFiles()) { Dungeon dungeon = DDungeon.create(this, file); if (dungeon != null) { dungeonRegistry.add(dungeon.getName(), dungeon); } else { MessageUtil.log(this, "&4The setup of dungeon &6" + file.getName() + "&4 is incorrect. See https://github.com/DRE2N/DungeonsXL/wiki/dungeon-configuration for reference."); } } } else if (DUNGEONS.listFiles().length != 0) { MessageUtil.log(this, "&4Multi floor dungeons are not part of the range of functions of this build."); } // Raw map to copy if (!DResourceWorld.RAW.exists()) { DResourceWorld.createRaw(); } /* Scripts & global data */ classRegistry = new Registry<>(); for (File script : FileUtil.getFilesForFolder(CLASSES)) { PlayerClass clss = new PlayerClass(xlib, script); classRegistry.add(clss.getName(), clss); } signScriptRegistry = new Registry<>(); for (File script : FileUtil.getFilesForFolder(SIGNS)) { SignScript sign = new SignScript(script); signScriptRegistry.add(sign.getName(), sign); } commandScriptRegistry = new Registry<>(); for (File script : FileUtil.getFilesForFolder(COMMANDS)) { CommandScript cmd = new CommandScript(script); commandScriptRegistry.add(cmd.getName(), cmd); } protections = new GlobalProtectionCache(this); protections.loadAll(); /* Integrations */ if (LWCUtil.isLWCLoaded()) { new LWCIntegration(this); } // Mobs - Supported providers externalMobProviderRegistry = new Registry<>(); for (ExternalMobPlugin externalMobPlugin : ExternalMobPlugin.values()) { externalMobProviderRegistry.add(externalMobPlugin.getIdentifier(), externalMobPlugin); } if (getServer().getPluginManager().getPlugin("Citizens") != null) { CitizensMobProvider citizensMobProvider = new CitizensMobProvider(this); externalMobProviderRegistry.add("CI", citizensMobProvider); getServer().getPluginManager().registerEvents(citizensMobProvider, this); } else { MessageUtil.log(this, "Could not find compatible Citizens plugin. The mob provider Citizens (\"CI\") will not get enabled..."); } // Mobs - Custom providers for (Entry customExternalMobProvider : mainConfig.getExternalMobProviders().entrySet()) { externalMobProviderRegistry.add(customExternalMobProvider.getKey(), new CustomExternalMobProvider(customExternalMobProvider)); } /* Players */ if (mainConfig.isSecureModeEnabled()) { new SecureModeTask(this).runTaskTimer(this, mainConfig.getSecureModeCheckInterval(), mainConfig.getSecureModeCheckInterval()); } playerCache = new PlayerCache(); playerGroupCache = new PlayerGroupCache(); gameCache = new ArrayList<>(); instanceCache = new Registry<>(); } public void init() { reload(); new BukkitRunnable() { @Override public void run() { playerCache.getAllInstancePlayers().forEach(p -> ((DInstancePlayer) p).update()); } }.runTaskTimer(this, 2L, 2L); /* Initialize listeners */ getServer().getPluginManager().registerEvents(new DWorldListener(this), this); getServer().getPluginManager().registerEvents(new GlobalProtectionListener(this), this); getServer().getPluginManager().registerEvents(new RewardListener(this), this); getServer().getPluginManager().registerEvents(new TriggerListener(this), this); getServer().getPluginManager().registerEvents(new DSignListener(this), this); getServer().getPluginManager().registerEvents(new DMobListener(this), this); getServer().getPluginManager().registerEvents(new DPlayerListener(this), this); } public void saveData() { protections.saveAll(); instanceCache.getAllIf(i -> i instanceof EditWorld).forEach(i -> ((DEditWorld) i).forceSave()); } public void checkState() { Bukkit.getOnlinePlayers().forEach(p -> new DGlobalPlayer(this, p)); for (File file : Bukkit.getWorldContainer().listFiles()) { if (!file.getName().startsWith("DXL_") || !file.isDirectory()) { continue; } if (file.getName().startsWith("DXL_Edit_")) { for (File mapFile : file.listFiles()) { if (!mapFile.getName().startsWith(".id_")) { continue; } String name = mapFile.getName().substring(4); File resource = new File(DungeonsXL.MAPS, name); File backup = new File(DungeonsXL.BACKUPS, resource.getName() + "-" + System.currentTimeMillis() + "_crashbackup"); FileUtil.copyDir(resource, backup); // Remove all files from the backupped resource world but not the config & data that we cannot fetch from the instance. remove: for (File remove : FileUtil.getFilesForFolder(resource)) { for (String nope : DungeonsXL.EXCLUDED_FILES) { if (remove.getName().equals(nope)) { continue remove; } } remove.delete(); } DResourceWorld.deleteUnusedFiles(file); FileUtil.copyDir(file, resource, DungeonsXL.EXCLUDED_FILES); } } FileUtil.removeDir(file); } } /* Getters and loaders */ /** * @return the plugin instance */ public static DungeonsXL getInstance() { return instance; } public PluginInit getInitializer() { return init; } @Override public XLib getXLib() { return xlib; } @Override public PlayerCache getPlayerCache() { return playerCache; } @Override public Collection getGameCache() { return gameCache; } @Override public Registry getClassRegistry() { return classRegistry; } @Override public Registry> getSignRegistry() { return signRegistry; } @Override public Registry> getRequirementRegistry() { return requirementRegistry; } @Override public Registry> getRewardRegistry() { return rewardRegistry; } @Override public Registry getDungeonRegistry() { return dungeonRegistry; } @Override public Registry getMapRegistry() { return mapRegistry; } @Override public Registry getInstanceCache() { return instanceCache; } @Override public Registry getGameRuleRegistry() { return gameRuleRegistry; } @Override public Registry> getTriggerRegistry() { return triggerRegistry; } @Override public Registry getExternalMobProviderRegistry() { return externalMobProviderRegistry; } @Override public Registry getGroupCache() { return playerGroupCache; } @Override public void registerModule(DungeonModule module) { modules.add(module); } @Override public void registerGroupAdapter(GroupAdapter groupAdapter) { if (mainConfig.areGroupAdaptersEnabled()) { groupAdapters.add(groupAdapter); } else { MessageUtil.log(this, "&4The group adapter &6" + groupAdapter.getClass().getName() + " &4was not registered because the feature is disabled."); } } /** * Returns a collection of the loadedGroupAdapters * * @return a collection of GroupAdapters */ public Collection getGroupAdapters() { return groupAdapters; } /** * Returns true if the plugin is not currently in the process of enabling or disabling or entirely disabled, otherwise false. * * @return true if the plugin is not currently in the process of enabling or disabling or entirely disabled, otherwise false */ public boolean isLoaded() { return loaded; } /** * Returns true if the plugin is currently loading a world, false if not. *

* If the plugin is loading a world, it is locked in order to prevent loading two at once. * * @return true if the plugin is currently loading a world, false if not */ public boolean isLoadingWorld() { return loadingWorld; } /** * Notifies the plugin that a world is being loaded. *

* If the plugin is loading a world, it is locked in order to prevent loading two at once. * * @param loadingWorld if a world is being loaded */ public void setLoadingWorld(boolean loadingWorld) { MessageUtil.debug(this, "World loading is now " + (loadingWorld ? "LOCKED" : "UNLOCKED")); this.loadingWorld = loadingWorld; } /** * Returns the command registry. * * @return the command registry */ public DCommandRegistry getCommandRegistry() { return (DCommandRegistry) init.getCommandRegistry(); } /** * @return the loaded instance of MainConfig */ public MainConfig getMainConfig() { return mainConfig; } /** * @return the loaded instance of GlobalProtectionCache */ public GlobalProtectionCache getGlobalProtectionCache() { return protections; } /** * Returns a registry of the loaded sign scripts. * * @return a registry of the loaded sign scripts */ public Registry getSignScriptRegistry() { return signScriptRegistry; } /** * Returns a registry of the loaded command scripts. * * @return a registry of the loaded command scripts */ public Registry getCommandScriptRegistry() { return commandScriptRegistry; } /* Object initialization */ @Override public PlayerGroup createGroup(Player leader) { return DGroup.create(this, GroupCreateEvent.Cause.CUSTOM, leader, null, null, null); } @Override public PlayerGroup createGroup(Player leader, PlayerGroup.Color color) { return DGroup.create(this, GroupCreateEvent.Cause.CUSTOM, leader, null, color, null); } @Override public PlayerGroup createGroup(Player leader, String name) { return DGroup.create(this, GroupCreateEvent.Cause.CUSTOM, leader, name, null, null); } @Override public PlayerGroup createGroup(Player leader, Dungeon dungeon) { return DGroup.create(this, GroupCreateEvent.Cause.CUSTOM, leader, null, null, dungeon); } @Override public PlayerGroup createGroup(Player leader, Collection members, String name, Dungeon dungeon) { PlayerGroup group = DGroup.create(this, GroupCreateEvent.Cause.CUSTOM, leader, name, null, dungeon); if (members != null) { members.forEach(group::addMember); } return group; } @Override public DungeonMob wrapEntity(LivingEntity entity, GameWorld gameWorld, String triggerId) { DungeonMob mob = getDungeonMob(entity); if (mob != null) { return mob; } else { return new DMob(entity, gameWorld, xlib.getExMob(triggerId), triggerId); } } @Override public DungeonMob wrapEntity(LivingEntity entity, GameWorld gameWorld, ExMob type) { DungeonMob mob = getDungeonMob(entity); if (mob != null) { return mob; } else { return new DMob(entity, gameWorld, type, type.getId()); } } @Override public DungeonMob wrapEntity(LivingEntity entity, GameWorld gameWorld, ExMob type, String triggerId) { DungeonMob mob = getDungeonMob(entity); if (mob != null) { return mob; } else { return new DMob(entity, gameWorld, type, triggerId); } } /* Getters */ @Override public DungeonMob getDungeonMob(LivingEntity entity) { GameWorld gameWorld = getGameWorld(entity.getWorld()); if (gameWorld == null) { return null; } for (DungeonMob mob : gameWorld.getMobs()) { if (mob.getEntity() == entity) { return mob; } } return null; } @Override public PlayerGroup getPlayerGroup(Player member) { for (PlayerGroup group : playerGroupCache) { if (group.getMembers().contains(member)) { return group; } } return null; } @Override public Game getGame(Player player) { for (Game game : gameCache) { if (game.getPlayers().contains(player)) { return game; } } return null; } @Override public Game getGame(World world) { GameWorld gameWorld = getGameWorld(world); return gameWorld != null ? gameWorld.getGame() : null; } @Override public GameWorld getGameWorld(World world) { InstanceWorld instance = getInstanceWorld(world); return instance instanceof GameWorld ? (GameWorld) instance : null; } @Override public EditWorld getEditWorld(World world) { InstanceWorld instance = getInstanceWorld(world); return instance instanceof EditWorld ? (EditWorld) instance : null; } public InstanceWorld getInstanceWorld(World world) { for (InstanceWorld instance : instanceCache) { if (world.equals(instance.getWorld())) { return instance; } } return null; } @Override public boolean isInstance(World world) { return world.getName().startsWith("DXL_Game_") || world.getName().startsWith("DXL_Edit_"); } @Override public boolean isDungeonItem(ItemStack itemStack) { if (!Version.isAtLeast(Version.MC1_16_5)) { return false; } if (itemStack == null || !itemStack.hasItemMeta()) { return false; } return itemStack.getItemMeta().getPersistentDataContainer().has(NamespacedKey.fromString("dungeon_item", this), PersistentDataType.BYTE); } @Override public ItemStack setDungeonItem(ItemStack itemStack, boolean dungeonItem) { if (!Version.isAtLeast(Version.MC1_16_5)) { return null; } if (itemStack == null || itemStack.getItemMeta() == null) { return null; } ItemStack dIStack = itemStack.clone(); ItemMeta meta = dIStack.getItemMeta(); NamespacedKey key = NamespacedKey.fromString("dungeon_item", this); if (dungeonItem) { meta.getPersistentDataContainer().set(key, PersistentDataType.BYTE, (byte) 1); } else { meta.getPersistentDataContainer().remove(key); } dIStack.setItemMeta(meta); return dIStack; } /** * Clean up all instances. */ public void deleteAllInstances() { BackupMode backupMode = mainConfig.getBackupMode(); for (InstanceWorld instance : instanceCache.getAll()) { if (backupMode == BackupMode.ON_DISABLE | backupMode == BackupMode.ON_DISABLE_AND_SAVE && instance instanceof EditWorld) { instance.getResource().backup(); } instance.delete(); } } /** * Checks if an old player wrapper instance of the user exists. If yes, the old Player of the user is replaced with the new object. * * @param player the player to check * @return if the player exists */ public boolean checkPlayer(Player player) { DGamePlayer dPlayer = (DGamePlayer) playerCache.getFirstGamePlayerIf(p -> p.getUniqueId().equals(player.getUniqueId())); if (dPlayer == null) { return false; } dPlayer.setPlayer(player); playerCache.remove(dPlayer); playerCache.add(player, dPlayer); dPlayer.setOfflineTimeMillis(0); return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/BreakCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class BreakCommand extends DCommand { public BreakCommand(DungeonsXL plugin) { super(plugin); setCommand("break"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_BREAK_HELP.getMessage()); setPermission(DPermission.BREAK.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; GlobalPlayer dGlobalPlayer = dPlayers.get(player); if (dGlobalPlayer.isInBreakMode()) { dGlobalPlayer.setInBreakMode(false); MessageUtil.sendMessage(sender, DMessage.CMD_BREAK_PROTECTED_MODE.getMessage()); } else { dGlobalPlayer.setInBreakMode(true); MessageUtil.sendMessage(sender, DMessage.CMD_BREAK_BREAK_MODE.getMessage()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/ChatCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DEditPlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class ChatCommand extends DCommand { public ChatCommand(DungeonsXL plugin) { super(plugin); setCommand("chat"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_CHAT_HELP.getMessage()); setPermission(DPermission.CHAT.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; GlobalPlayer dPlayer = dPlayers.get(player); if (plugin.getPlayerGroup(player) == null && !(dPlayer instanceof DEditPlayer)) { MessageUtil.sendMessage(player, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } dPlayer.setInGroupChat(!dPlayer.isInGroupChat()); MessageUtil.sendMessage(player, (dPlayer.isInGroupChat() ? DMessage.CMD_CHAT_DUNGEON_CHAT : DMessage.CMD_CHAT_NORMAL_CHAT).getMessage()); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/ChatSpyCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class ChatSpyCommand extends DCommand { public ChatSpyCommand(DungeonsXL plugin) { super(plugin); setCommand("chatSpy"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_CHATSPY_HELP.getMessage()); setPermission(DPermission.CHAT_SPY.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; GlobalPlayer dPlayer = dPlayers.get(player); dPlayer.setInChatSpyMode(!dPlayer.isInChatSpyMode()); MessageUtil.sendMessage(player, (dPlayer.isInChatSpyMode() ? DMessage.CMD_CHATSPY_STARTED : DMessage.CMD_CHATSPY_STOPPED).getMessage()); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/CreateCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DDungeon; import de.erethon.dungeonsxl.player.DEditPlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.world.DEditWorld; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.xlib.chat.MessageUtil; import java.io.File; import org.bukkit.command.CommandSender; import org.bukkit.command.ConsoleCommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class CreateCommand extends DCommand { public CreateCommand(DungeonsXL plugin) { super(plugin); setMinArgs(1); setMaxArgs(1); setCommand("create"); setHelp(DMessage.CMD_CREATE_HELP.getMessage()); setPermission(DPermission.CREATE.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { String name = args[1]; if (new File(DungeonsXL.MAPS, name).exists()) { MessageUtil.sendMessage(sender, DMessage.ERROR_NAME_IN_USE.getMessage(name)); return; } if (name.length() > 15) { MessageUtil.sendMessage(sender, DMessage.ERROR_NAME_TOO_LONG.getMessage()); return; } if (sender instanceof ConsoleCommandSender) { MessageUtil.log(plugin, "&6Creating new map."); MessageUtil.log(plugin, "&6Generating new world..."); DResourceWorld resource = new DResourceWorld(plugin, name); plugin.getMapRegistry().add(name, resource); DEditWorld editWorld = resource.generate(); editWorld.save(); editWorld.delete(); MessageUtil.log(plugin, "&6World generation finished."); } else if (sender instanceof Player) { Player player = (Player) sender; if (dPlayers.getGamePlayer(player) != null) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); return; } MessageUtil.log(plugin, "&6Creating new map."); MessageUtil.log(plugin, "&6Generating new world..."); DResourceWorld resource = new DResourceWorld(plugin, name); plugin.getMapRegistry().add(name, resource); plugin.getDungeonRegistry().add(name, new DDungeon(plugin, resource)); DEditWorld editWorld = resource.generate(); MessageUtil.log(plugin, "&6World generation finished."); new DEditPlayer(plugin, player, editWorld); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/DCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.PlayerCache; import de.erethon.dungeonsxl.config.MainConfig; import de.erethon.xlib.XLib; import de.erethon.xlib.command.DRECommand; /** * @author Daniel Saukel */ public abstract class DCommand extends DRECommand { protected DungeonsXL plugin; protected XLib xlib; protected MainConfig config; protected PlayerCache dPlayers; protected DCommand(DungeonsXL plugin) { this.plugin = plugin; xlib = plugin.getXLib(); config = plugin.getMainConfig(); dPlayers = plugin.getPlayerCache(); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/DCommandRegistry.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.xlib.command.DRECommandRegistry; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.plugin.PluginInit; /** * An enumeration of all command instances. * * @author Daniel Saukel */ public class DCommandRegistry extends DRECommandRegistry { public static final String LABEL = "dungeonsxl"; public BreakCommand breakCmd; public ChatCommand chat; public ChatSpyCommand chatSpy; public CreateCommand create; public EditCommand edit; public EnterCommand enter; public EscapeCommand escape; public DeleteCommand delete; public DungeonItemCommand dungeonItem; public GameCommand game; public GroupCommand group; public HelpCommand help; public ImportCommand importCmd; public InviteCommand invite; public KickCommand kick; public LeaveCommand leave; public ListCommand list; public LivesCommand lives; public MainCommand main; public MsgCommand message; public PlayCommand play; public PortalCommand portal; public ReloadCommand reload; public RenameCommand rename; public ResourcePackCommand resourcePack; public SaveCommand save; public StatusCommand status; public TestCommand test; public UninviteCommand uninvite; public DCommandRegistry(DungeonsXL plugin, PluginInit init) { super(LABEL, init); breakCmd = new BreakCommand(plugin); chat = new ChatCommand(plugin); chatSpy = new ChatSpyCommand(plugin); create = new CreateCommand(plugin); edit = new EditCommand(plugin); enter = new EnterCommand(plugin); escape = new EscapeCommand(plugin); delete = new DeleteCommand(plugin); dungeonItem = new DungeonItemCommand(plugin); game = new GameCommand(plugin); group = new GroupCommand(plugin); help = new HelpCommand(plugin); importCmd = new ImportCommand(plugin); invite = new InviteCommand(plugin); kick = new KickCommand(plugin); leave = new LeaveCommand(plugin); list = new ListCommand(plugin); lives = new LivesCommand(plugin); main = new MainCommand(plugin); message = new MsgCommand(plugin); play = new PlayCommand(plugin); portal = new PortalCommand(plugin); reload = new ReloadCommand(plugin); rename = new RenameCommand(plugin); resourcePack = new ResourcePackCommand(plugin); save = new SaveCommand(plugin); status = new StatusCommand(plugin); test = new TestCommand(plugin); uninvite = new UninviteCommand(plugin); addCommand(breakCmd); addCommand(create); addCommand(delete); if (Version.isAtLeast(Version.MC1_16_5)) { addCommand(dungeonItem); } addCommand(edit); addCommand(enter); addCommand(escape); addCommand(game); addCommand(group); addCommand(help); addCommand(importCmd); addCommand(invite); addCommand(kick); addCommand(leave); addCommand(list); addCommand(lives); addCommand(main); addCommand(message); addCommand(play); addCommand(portal); addCommand(reload); addCommand(rename); addCommand(resourcePack); addCommand(save); addCommand(status); addCommand(test); addCommand(uninvite); if (plugin.getMainConfig().isChatEnabled()) { addCommand(chat); addCommand(chatSpy); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/DeleteCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DDungeon; import de.erethon.dungeonsxl.dungeon.DungeonConfig; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.FileUtil; import java.io.File; import java.util.ArrayList; import java.util.List; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class DeleteCommand extends DCommand { public DeleteCommand(DungeonsXL plugin) { super(plugin); setCommand("delete"); setMinArgs(1); setMaxArgs(2); setHelp(DMessage.CMD_DELETE_HELP.getMessage()); setPermission(DPermission.DELETE.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { ResourceWorld resource = plugin.getMapRegistry().get(args[1]); if (resource == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_MAP.getMessage(args[1])); return; } if (args.length == 2 && sender instanceof Player) { ClickEvent onClickConfirm = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/dungeonsxl delete " + args[1] + " true"); TextComponent confirm = new TextComponent(DMessage.BUTTON_ACCEPT.getMessage()); confirm.setClickEvent(onClickConfirm); ClickEvent onClickDeny = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/dungeonsxl delete " + args[1] + " false"); TextComponent deny = new TextComponent(DMessage.BUTTON_DENY.getMessage()); deny.setClickEvent(onClickDeny); MessageUtil.sendMessage(sender, DMessage.CMD_DELETE_BACKUPS.getMessage()); ((Player) sender).spigot().sendMessage(confirm, new TextComponent(" "), deny); return; } if (resource.getEditWorld() != null) { resource.getEditWorld().delete(false); } plugin.getMapRegistry().remove(resource); FileUtil.removeDir(resource.getFolder()); if (args[2].equalsIgnoreCase("true")) { for (File file : DungeonsXL.BACKUPS.listFiles()) { if (file.getName().startsWith(resource.getName() + "-")) { FileUtil.removeDir(file); } } } List toRemove = new ArrayList<>(); for (Dungeon dungeon : plugin.getDungeonRegistry()) { if (dungeon.getStartFloor().equals(resource)) { toRemove.add(dungeon); if (dungeon.isMultiFloor()) { ((DDungeon) dungeon).getConfig().getFile().delete(); } } else if (dungeon.isMultiFloor() && dungeon.getEndFloor().equals(resource)) { toRemove.add(dungeon); ((DDungeon) dungeon).getConfig().getFile().delete(); } else if (dungeon.isMultiFloor() && dungeon.getFloors().contains(resource)) { dungeon.removeFloor(resource); DungeonConfig config = ((DDungeon) dungeon).getConfig(); List floors = config.getConfig().getStringList("floors"); floors.remove(resource.getName()); config.getConfig().set("floors", floors); config.save(); } } toRemove.forEach(plugin.getDungeonRegistry()::remove); MessageUtil.sendMessage(sender, DMessage.CMD_DELETE_SUCCESS.getMessage(args[1])); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/DungeonItemCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; /** * @author Frank Baumann, Daniel Saukel */ public class DungeonItemCommand extends DCommand { public DungeonItemCommand(DungeonsXL plugin) { super(plugin); setCommand("dungeonItem"); setAliases("di"); setMinArgs(0); setMaxArgs(1); setHelp(DMessage.CMD_DUNGEON_ITEM_HELP.getMessage()); setPermission(DPermission.DUNGEON_ITEM.getNode()); setPlayerCommand(true); setConsoleCommand(false); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; PlayerInventory inv = player.getInventory(); ItemStack itemStack = inv.getItemInMainHand(); if (itemStack.getType().isAir()) { MessageUtil.sendTitleMessage(player, DMessage.ERROR_NO_ITEM_IN_MAIN_HAND.getMessage()); return; } String action = args.length >= 2 ? args[1] : "info"; if (action.equalsIgnoreCase("true")) { inv.setItemInMainHand(plugin.setDungeonItem(itemStack, true)); MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_SET_DUNGEON.getMessage()); MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_DUNGEON_ITEM_HELP.getMessage()); } else if (action.equalsIgnoreCase("false")) { inv.setItemInMainHand(plugin.setDungeonItem(itemStack, false)); MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_SET_GLOBAL.getMessage()); MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_GLOBAL_ITEM_HELP.getMessage()); } else { if (plugin.isDungeonItem(itemStack)) { MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_INFO_DUNGEON.getMessage()); MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_DUNGEON_ITEM_HELP.getMessage()); } else { MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_INFO_GLOBAL.getMessage()); MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_GLOBAL_ITEM_HELP.getMessage()); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/EditCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.player.EditPlayerEditEvent; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DEditPlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.config.CommonMessage; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class EditCommand extends DCommand { public EditCommand(DungeonsXL plugin) { super(plugin); setCommand("edit"); setMinArgs(1); setMaxArgs(1); setHelp(DMessage.CMD_EDIT_HELP.getMessage()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; ResourceWorld resource = plugin.getMapRegistry().getFirstIf(d -> d.getName().equalsIgnoreCase(args[1])); if (resource == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_MAP.getMessage(args[1])); return; } if (!resource.isInvitedPlayer(player) && !DPermission.hasPermission(player, DPermission.EDIT)) { MessageUtil.sendMessage(player, CommonMessage.CMD_NO_PERMISSION.getMessage()); return; } boolean newlyLoaded = resource.getEditWorld() == null; EditWorld editWorld = resource.getOrInstantiateEditWorld(false); if (editWorld == null) { MessageUtil.sendMessage(player, DMessage.ERROR_TOO_MANY_INSTANCES.getMessage()); return; } PlayerGroup dGroup = plugin.getPlayerGroup(player); GlobalPlayer dPlayer = dPlayers.get(player); if (dPlayer instanceof InstancePlayer) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); return; } if (dGroup != null) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_GROUP.getMessage()); return; } Bukkit.getPluginManager().callEvent(new EditPlayerEditEvent(new DEditPlayer(plugin, player, editWorld), newlyLoaded)); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/EnterCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent.Cause; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class EnterCommand extends DCommand { public EnterCommand(DungeonsXL plugin) { super(plugin); setMinArgs(1); setMaxArgs(2); setCommand("enter"); setHelp(DMessage.CMD_ENTER_HELP.getMessage()); setPermission(DPermission.ENTER.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player captain = (Player) sender; String targetName = args.length == 3 ? args[2] : args[1]; PlayerGroup joining = args.length == 3 ? plugin.getGroupCache().get(args[1]) : plugin.getPlayerGroup(captain); PlayerGroup target = plugin.getGroupCache().get(targetName); if (target == null) { Player targetPlayer = Bukkit.getPlayer(targetName); if (targetPlayer != null) { target = plugin.getPlayerGroup(targetPlayer); } } if (target == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_GROUP.getMessage(targetName)); return; } Game game = target.getGame(); if (game == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NOT_IN_GAME.getMessage(targetName)); return; } if (joining != null && joining.getGame() != null) { MessageUtil.sendMessage(sender, DMessage.ERROR_LEAVE_GAME.getMessage()); return; } if (joining == null) { joining = DGroup.create(plugin, Cause.COMMAND, captain, null, null, game.getDungeon()); } if (joining == null) { return; } if (joining.getLeader() != captain && !DPermission.hasPermission(sender, DPermission.BYPASS)) { MessageUtil.sendMessage(sender, DMessage.ERROR_NOT_LEADER.getMessage()); return; } game.addGroup(joining); joining.sendMessage(DMessage.CMD_ENTER_SUCCESS.getMessage(joining.getName(), target.getName())); for (Player player : joining.getMembers().getOnlinePlayers()) { new DGamePlayer(plugin, player, game.getWorld()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/EscapeCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.player.EditPlayerLeaveEvent; import de.erethon.dungeonsxl.api.player.EditPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Milan Albrecht, Daniel Saukel */ public class EscapeCommand extends DCommand { public EscapeCommand(DungeonsXL plugin) { super(plugin); setCommand("escape"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_ESCAPE_HELP.getMessage()); setPermission(DPermission.ESCAPE.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; EditPlayer editPlayer = dPlayers.getEditPlayer(player); if (dPlayers.getGamePlayer(player) != null) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); } else if (editPlayer != null) { EditPlayerLeaveEvent event = new EditPlayerLeaveEvent(editPlayer, true, true); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } editPlayer.escape(); EditWorld editWorld = editPlayer.getEditWorld(); if (editWorld == null) { return; } if (editWorld.getWorld().getPlayers().isEmpty() && event.getUnloadIfEmpty()) { editWorld.delete(false); } } else { PlayerGroup dGroup = plugin.getPlayerGroup(player); if (dGroup != null) { dGroup.removeMember(player); MessageUtil.sendMessage(player, DMessage.CMD_LEAVE_SUCCESS.getMessage()); return; } MessageUtil.sendMessage(player, DMessage.ERROR_NOT_IN_DUNGEON.getMessage()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/GameCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class GameCommand extends DCommand { public GameCommand(DungeonsXL plugin) { super(plugin); setCommand("game"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_GAME_HELP.getMessage()); setPermission(DPermission.GAME.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; PlayerGroup dGroup = plugin.getPlayerGroup(player); if (dGroup == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } GameWorld gameWorld = dGroup.getGameWorld(); if (gameWorld == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_GAME.getMessage()); return; } DGame game = (DGame) gameWorld.getGame(); if (game == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_GAME.getMessage()); return; } MessageUtil.sendCenteredMessage(sender, "&4&l[ &6Game &4&l]"); String groups = ""; for (PlayerGroup group : game.getGroups()) { groups += (group == game.getGroups().get(0) ? "" : "&b, &e") + group.getName(); } MessageUtil.sendMessage(sender, "&bGroups: &e" + groups); MessageUtil.sendMessage(sender, "&bDungeon: &e" + (dGroup.getDungeon().getName() == null ? "N/A" : dGroup.getDungeon().getName())); MessageUtil.sendMessage(sender, "&bMap: &e" + (dGroup.getGameWorld() == null ? "N/A" : dGroup.getGameWorld().getName())); MessageUtil.sendMessage(sender, "&bWaves finished: &e" + game.getWaveCount()); MessageUtil.sendMessage(sender, "&bKills: &e" + game.getGameKills() + " / Game; " + game.getWaveKills() + " / Wave"); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/GroupCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent.Cause; import de.erethon.dungeonsxl.api.event.group.GroupDisbandEvent; import de.erethon.dungeonsxl.api.event.group.GroupPlayerKickEvent; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class GroupCommand extends DCommand { public GroupCommand(DungeonsXL plugin) { super(plugin); setCommand("group"); setMinArgs(0); setMaxArgs(2); setHelp(DMessage.CMD_GROUP_HELP_MAIN.getMessage()); setPermission(DPermission.GROUP.getNode()); setPlayerCommand(true); } private CommandSender sender; private Player player; private String[] args; @Override public void onExecute(String[] args, CommandSender sender) { this.sender = sender; this.player = (Player) sender; this.args = args; DGroup dGroup = (DGroup) plugin.getPlayerGroup(player); if (args.length == 2) { if (args[1].equalsIgnoreCase("disband")) { disbandGroup(dGroup, null); return; } else if (args[1].equalsIgnoreCase("show")) { showGroup(dGroup); return; } } else if (args.length >= 3) { if (args[1].equalsIgnoreCase("kick")) { kickPlayer(dGroup); return; } else if (args[1].equalsIgnoreCase("invite")) { invitePlayer(dGroup); return; } else if (args[1].equalsIgnoreCase("uninvite")) { uninvitePlayer(dGroup); return; } else if (args[1].equalsIgnoreCase("help")) { showHelp(args[2]); return; } else if (args[1].equalsIgnoreCase("create")) { createGroup(); return; } else if (args[1].equalsIgnoreCase("disband") && DPermission.hasPermission(sender, DPermission.GROUP_ADMIN)) { disbandGroup((DGroup) plugin.getGroupCache().get(args[2]), args[2]); return; } else if (args[1].equalsIgnoreCase("join")) { joinGroup((DGroup) plugin.getGroupCache().get(args[2])); return; } else if (args[1].equalsIgnoreCase("show") && DPermission.hasPermission(sender, DPermission.GROUP_ADMIN)) { DGroup group = (DGroup) plugin.getGroupCache().get(args[2]); Player player = Bukkit.getPlayer(args[2]); if (group == null && player != null) { group = (DGroup) plugin.getPlayerGroup(player); } showGroup(group); return; } } showHelp("1"); } public void createGroup() { if (plugin.getPlayerGroup(player) != null) { MessageUtil.sendMessage(sender, DMessage.ERROR_LEAVE_GROUP.getMessage()); return; } if (plugin.getGroupCache().get(args[2]) != null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NAME_IN_USE.getMessage(args[2])); return; } DGroup dGroup = DGroup.create(plugin, Cause.COMMAND, player, args[2], null, null); if (dGroup != null) { MessageUtil.sendMessage(sender, DMessage.GROUP_CREATED.getMessage(sender.getName(), args[2])); } } public void disbandGroup(DGroup dGroup, String name) { if (dGroup == null) { // only gets here MessageUtil.sendMessage(sender, name == null ? DMessage.ERROR_SELF_NOT_IN_GROUP.getMessage() : DMessage.ERROR_NO_SUCH_GROUP.getMessage(name)); return; } if (dGroup.isPlaying()) { MessageUtil.sendMessage(sender, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); return; } GroupDisbandEvent event = new GroupDisbandEvent(dGroup, plugin.getPlayerCache().get(player), GroupDisbandEvent.Cause.COMMAND); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { dGroup.delete(); MessageUtil.sendMessage(sender, DMessage.GROUP_DISBANDED.getMessage(sender.getName(), dGroup.getName())); dGroup = null; } } public void invitePlayer(DGroup dGroup) { if (dGroup == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } Player toInvite = Bukkit.getPlayer(args[2]); if (toInvite != null) { dGroup.addInvitedPlayer(toInvite, false); } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[2])); } } public void uninvitePlayer(DGroup dGroup) { if (dGroup == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } dGroup.clearOfflineInvitedPlayers(); Player toUninvite = Bukkit.getPlayer(args[2]); if (toUninvite != null) { if (dGroup.getInvitedPlayers().contains(toUninvite)) { dGroup.removeInvitedPlayer(toUninvite, false); } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NOT_IN_GROUP.getMessage(args[2])); } } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[2])); } } public void joinGroup(DGroup dGroup) { if (dGroup == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_GROUP.getMessage(args[2])); return; } if (plugin.getPlayerGroup(player) != null) { MessageUtil.sendMessage(sender, DMessage.ERROR_LEAVE_GROUP.getMessage()); return; } if (dGroup.isPlaying()) { MessageUtil.sendMessage(sender, DMessage.ERROR_GROUP_IS_PLAYING.getMessage()); return; } if (!dGroup.getInvitedPlayers().contains(player) && !DPermission.hasPermission(player, DPermission.BYPASS)) { MessageUtil.sendMessage(sender, DMessage.ERROR_NOT_INVITED.getMessage(args[2])); return; } dGroup.addMember(player); dGroup.removeInvitedPlayer(player, true); } public void kickPlayer(DGroup dGroup) { if (dGroup == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_JOIN_GROUP.getMessage()); } Player toKick = Bukkit.getPlayer(args[2]); if (toKick != null) { GroupPlayerKickEvent event = new GroupPlayerKickEvent(dGroup, dPlayers.get(toKick.getPlayer()), GroupPlayerKickEvent.Cause.COMMAND); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { if (dGroup.getMembers().contains(toKick)) { dGroup.removeMember(toKick); MessageUtil.sendMessage(sender, DMessage.GROUP_KICKED_PLAYER.getMessage(sender.getName(), args[2], dGroup.getName())); } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NOT_IN_GROUP.getMessage(args[2], dGroup.getName())); } } } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[2])); } } public void showGroup(DGroup dGroup) { if (dGroup == null) { if (args.length == 3) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_GROUP.getMessage(args[2])); } else if (args.length == 2) { MessageUtil.sendMessage(sender, DMessage.ERROR_JOIN_GROUP.getMessage()); } return; } MessageUtil.sendCenteredMessage(sender, "&4&l[ &6" + dGroup.getName() + " &4&l]"); MessageUtil.sendMessage(sender, "&bCaptain: &e" + dGroup.getLeader().getName()); String players = ""; for (String player : dGroup.getMembers().getNames()) { players += (players.isEmpty() ? "" : "&b, &e") + player; } MessageUtil.sendMessage(sender, "&bPlayers: &e" + players); MessageUtil.sendMessage(sender, "&bDungeon: &e" + (dGroup.getDungeonName() == null ? "N/A" : dGroup.getDungeonName())); MessageUtil.sendMessage(sender, "&bMap: &e" + (dGroup.getMapName() == null ? "N/A" : dGroup.getMapName())); MessageUtil.sendMessage(sender, "&bScore: &e" + (dGroup.getScore() == 0 ? "N/A" : dGroup.getScore())); MessageUtil.sendMessage(sender, "&bLives: &e" + (dGroup.getLives() == -1 ? "N/A" : dGroup.getLives())); } public void showHelp(String page) { MessageUtil.sendPluginTag(sender, plugin); switch (page) { default: MessageUtil.sendCenteredMessage(sender, "&4&l[ &61-5 &4/ &67 &4| &61 &4&l]"); MessageUtil.sendMessage(sender, "&bcreate" + "&7 - " + DMessage.CMD_GROUP_HELP_CREATE.getMessage()); MessageUtil.sendMessage(sender, "&bdisband" + "&7 - " + DMessage.CMD_GROUP_HELP_DISBAND.getMessage()); MessageUtil.sendMessage(sender, "&binvite" + "&7 - " + DMessage.CMD_GROUP_HELP_INVITE.getMessage()); MessageUtil.sendMessage(sender, "&buninvite" + "&7 - " + DMessage.CMD_GROUP_HELP_UNINVITE.getMessage()); MessageUtil.sendMessage(sender, "&bjoin" + "&7 - " + DMessage.CMD_GROUP_HELP_JOIN.getMessage()); break; case "2": MessageUtil.sendCenteredMessage(sender, "&4&l[ &66-10 &4/ &67 &4| &62 &4&l]"); MessageUtil.sendMessage(sender, "&bkick" + "&7 - " + DMessage.CMD_GROUP_HELP_KICK.getMessage()); MessageUtil.sendMessage(sender, "&bshow" + "&7 - " + DMessage.CMD_GROUP_HELP_SHOW.getMessage()); break; } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/HelpCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.command.DRECommand; import de.erethon.xlib.util.NumberUtil; import java.util.ArrayList; import java.util.Set; import org.bukkit.command.CommandSender; /** * @author Frank Baumann, Daniel Saukel */ public class HelpCommand extends DCommand { public HelpCommand(DungeonsXL plugin) { super(plugin); setCommand("help"); setMinArgs(0); setMaxArgs(1); setHelp(DMessage.CMD_HELP_HELP.getMessage()); setPermission(DPermission.HELP.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Set dCommandList = plugin.getCommandRegistry().getCommands(); ArrayList toSend = new ArrayList<>(); int page = 1; if (args.length == 2) { page = NumberUtil.parseInt(args[1], 1); } int send = 0; int max = 0; int min = 0; for (DRECommand dCommand : dCommandList) { send++; if (send >= page * 5 - 4 && send <= page * 5) { min = page * 5 - 4; max = page * 5; toSend.add(dCommand); } } MessageUtil.sendPluginTag(sender, plugin); MessageUtil.sendCenteredMessage(sender, "&4&l[ &6" + min + "-" + max + " &4/&6 " + send + " &4|&6 " + page + " &4&l]"); for (DRECommand dCommand : toSend) { MessageUtil.sendMessage(sender, "&b" + dCommand.getCommand() + "&7 - " + dCommand.getHelp()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/ImportCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DDungeon; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.FileUtil; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.dungeonsxl.world.WorldConfig; import java.io.File; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.command.CommandSender; /** * @author Frank Baumann, Daniel Saukel */ public class ImportCommand extends DCommand { public ImportCommand(DungeonsXL plugin) { super(plugin); setMinArgs(1); setMaxArgs(1); setCommand("import"); setHelp(DMessage.CMD_IMPORT_HELP.getMessage()); setPermission(DPermission.IMPORT.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { File target = new File(DungeonsXL.MAPS, args[1]); File source = new File(Bukkit.getWorldContainer(), args[1]); if (!source.exists()) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_MAP.getMessage(args[1])); return; } if (target.exists()) { MessageUtil.sendMessage(sender, DMessage.ERROR_NAME_IN_USE.getMessage(args[1])); return; } World world = Bukkit.getWorld(args[1]); if (world != null) { world.save(); } MessageUtil.log(plugin, "&6Creating new map."); MessageUtil.log(plugin, "&6Importing world..."); FileUtil.copyDir(source, target, "playerdata", "stats"); DResourceWorld resource = new DResourceWorld(plugin, args[1]); plugin.getDungeonRegistry().add(args[1], new DDungeon(plugin, resource)); if (world != null && world.getEnvironment() != Environment.NORMAL) { WorldConfig config = resource.getConfig(true); config.setWorldEnvironment(world.getEnvironment()); config.save(); } plugin.getMapRegistry().add(resource.getName(), resource); MessageUtil.sendMessage(sender, DMessage.CMD_IMPORT_SUCCESS.getMessage(args[1])); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/InviteCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; /** * @author Frank Baumann, Daniel Saukel */ public class InviteCommand extends DCommand { public InviteCommand(DungeonsXL plugin) { super(plugin); setMinArgs(2); setMaxArgs(2); setCommand("invite"); setHelp(DMessage.CMD_INVITE_HELP.getMessage()); setPermission(DPermission.INVITE.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { ResourceWorld resource = plugin.getMapRegistry().get(args[2]); OfflinePlayer player = Bukkit.getOfflinePlayer(args[1]); if (resource != null) { if (player != null) { resource.addInvitedPlayer(player); MessageUtil.sendMessage(sender, DMessage.CMD_INVITE_SUCCESS.getMessage(args[1], args[2])); } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[2])); } } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_MAP.getMessage(args[2])); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/KickCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class KickCommand extends DCommand { public KickCommand(DungeonsXL plugin) { super(plugin); setCommand("kick"); setMinArgs(1); setMaxArgs(1); setHelp(DMessage.CMD_KICK_HELP.getMessage()); setPermission(DPermission.KICK.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = Bukkit.getPlayer(args[1]); if (player != null) { plugin.getCommandRegistry().leave.onExecute(new String[]{plugin.getCommandRegistry().leave.getCommand()}, player); MessageUtil.sendMessage(sender, DMessage.CMD_KICK_SUCCESS.getMessage(player.getName())); } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[1])); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/LeaveCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.event.group.GroupPlayerLeaveEvent; import de.erethon.dungeonsxl.api.event.player.EditPlayerLeaveEvent; import de.erethon.dungeonsxl.api.player.EditPlayer; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class LeaveCommand extends DCommand { public LeaveCommand(DungeonsXL plugin) { super(plugin); setCommand("leave"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_LEAVE_HELP.getMessage()); setPermission(DPermission.LEAVE.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; GlobalPlayer globalPlayer = dPlayers.get(player); Game game = plugin.getGame(player); if (game != null && game.isTutorial()) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_LEAVE_IN_TUTORIAL.getMessage()); return; } PlayerGroup group = globalPlayer.getGroup(); if (group == null && !(globalPlayer instanceof EditPlayer)) { MessageUtil.sendMessage(player, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } if (globalPlayer instanceof GamePlayer) { GroupPlayerLeaveEvent event = new GroupPlayerLeaveEvent(group, globalPlayer); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } ((GamePlayer) globalPlayer).leave(); } else if (globalPlayer instanceof EditPlayer) { EditPlayerLeaveEvent event = new EditPlayerLeaveEvent((EditPlayer) globalPlayer, false, true); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } ((EditPlayer) globalPlayer).leave(event.getUnloadIfEmpty()); } else { group.removeMember(player); } MessageUtil.sendMessage(player, DMessage.CMD_LEAVE_SUCCESS.getMessage()); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/ListCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DungeonConfig; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.NumberUtil; import de.erethon.dungeonsxl.world.DResourceWorld; import java.io.File; import java.util.ArrayList; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class ListCommand extends DCommand { public ListCommand(DungeonsXL plugin) { super(plugin); setCommand("list"); setMinArgs(0); setMaxArgs(3); setHelp(DMessage.CMD_LIST_HELP.getMessage()); setPermission(DPermission.LIST.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { ArrayList dungeonList = new ArrayList<>(); for (Dungeon dungeon : plugin.getDungeonRegistry()) { dungeonList.add(dungeon.getName()); } ArrayList mapList = new ArrayList<>(); for (File file : DungeonsXL.MAPS.listFiles()) { if (!file.equals(DResourceWorld.RAW)) { mapList.add(file.getName()); } } ArrayList loadedList = new ArrayList<>(); for (InstanceWorld editWorld : plugin.getInstanceCache().getAllIf(i -> i instanceof EditWorld)) { loadedList.add(editWorld.getWorld().getWorldFolder().getName() + " / " + editWorld.getName()); } for (InstanceWorld gameWorld : plugin.getInstanceCache().getAllIf(i -> i instanceof GameWorld)) { loadedList.add(gameWorld.getWorld().getWorldFolder().getName() + " / " + gameWorld.getName()); } ArrayList toSend = new ArrayList<>(); ArrayList stringList = mapList; boolean specified = false; byte listType = 0; if (args.length >= 2) { if (args[1].equalsIgnoreCase("dungeons") || args[1].equalsIgnoreCase("d")) { if (args.length >= 3) { Dungeon dungeon = plugin.getDungeonRegistry().get(args[2]); if (dungeon != null) { MessageUtil.sendPluginTag(sender, plugin); MessageUtil.sendCenteredMessage(sender, "&4&l[ &6" + dungeon.getName() + " &4&l]"); StringBuilder floorList = new StringBuilder(); for (int i = 0; i < dungeon.getFloors().size(); i++) { if (i != 0) { floorList.append(", "); } floorList.append(dungeon.getFloors().get(i).getName()); } MessageUtil.sendMessage(sender, "&eFloors: &o" + floorList.toString()); MessageUtil.sendMessage(sender, "&estartFloor: &o[" + dungeon.getStartFloor().getName() + "]"); MessageUtil.sendMessage(sender, "&eendFloor: &o[" + dungeon.getEndFloor().getName() + "]"); MessageUtil.sendMessage(sender, "&efloorCount: &o[" + dungeon.getFloorCount() + "]"); MessageUtil.sendMessage(sender, "&eremoveWhenPlayed: &o[" + dungeon.getRemoveWhenPlayed() + "]"); return; } } specified = true; stringList = dungeonList; listType = 1; } else if (args[1].equalsIgnoreCase("maps") || args[1].equalsIgnoreCase("m")) { specified = true; } else if (args[1].equalsIgnoreCase("loaded") || args[1].equalsIgnoreCase("l")) { specified = true; stringList = loadedList; listType = 2; } } int page = 1; if (args.length == 3) { page = NumberUtil.parseInt(args[2], 1); } else if (args.length == 2 & !specified) { page = NumberUtil.parseInt(args[1], 1); } int send = 0; int max = 0; int min = 0; for (String string : stringList) { send++; if (send >= page * 5 - 4 && send <= page * 5) { min = page * 5 - 4; max = page * 5; toSend.add(string); } } MessageUtil.sendPluginTag(sender, plugin); MessageUtil.sendCenteredMessage(sender, "&4&l[ &6" + min + "-" + max + " &4/&6 " + send + " &4|&6 " + page + " &4&l]"); switch (listType) { case 0: MessageUtil.sendMessage(sender, "&4Map&7 | &eInvited"); for (String map : toSend) { boolean invited = false; if (sender instanceof Player) { ResourceWorld resource = plugin.getMapRegistry().get(map); if (resource != null) { invited = resource.isInvitedPlayer((Player) sender); } } MessageUtil.sendMessage(sender, "&b" + map + "&7 | &e" + invited); } break; case 1: MessageUtil.sendMessage(sender, "&4Dungeon&7 | &eMap count"); for (String dungeon : toSend) { DungeonConfig dungeonConfig = new DungeonConfig(plugin, new File(DungeonsXL.DUNGEONS, dungeon + ".yml")); int count = dungeonConfig.getFloors().size() + 2; MessageUtil.sendMessage(sender, "&b" + dungeon + "&7 | &e" + count); } break; case 2: MessageUtil.sendMessage(sender, "&4Loaded map"); for (String map : toSend) { MessageUtil.sendMessage(sender, "&b" + map); } break; default: break; } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/LivesCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.config.CommonMessage; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class LivesCommand extends DCommand { public LivesCommand(DungeonsXL plugin) { super(plugin); setCommand("lives"); setMinArgs(0); setMaxArgs(1); setHelp(DMessage.CMD_LIVES_HELP.getMessage()); setPermission(DPermission.LIVES.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = null; if (args.length == 2) { if (Bukkit.getPlayer(args[1]) != null) { player = Bukkit.getPlayer(args[1]); } } else if (sender instanceof Player) { player = (Player) sender; } else { MessageUtil.sendMessage(sender, CommonMessage.CMD_NO_CONSOLE_COMMAND.getMessage(getCommand())); return; } GlobalPlayer globalPlayer = dPlayers.get(player); if (!(globalPlayer instanceof GamePlayer)) { MessageUtil.sendMessage(sender, args.length == 1 ? DMessage.ERROR_NO_GAME.getMessage() : DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[1])); return; } GamePlayer gamePlayer = (GamePlayer) globalPlayer; PlayerGroup group = gamePlayer != null ? gamePlayer.getGroup() : plugin.getGroupCache().get(args[1]); if (gamePlayer != null) { MessageUtil.sendMessage(sender, DMessage.CMD_LIVES_PLAYER.getMessage(gamePlayer.getName(), gamePlayer.getLives() == -1 ? DMessage.PLAYER_UNLIMITED_LIVES.getMessage() : String.valueOf(gamePlayer.getLives()))); } else if (group != null) { MessageUtil.sendMessage(sender, DMessage.CMD_LIVES_GROUP.getMessage(group.getName(), String.valueOf(group.getLives() == -1 ? "UNLIMITED" : group.getLives()))); } else { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_PLAYER.getMessage(args[1])); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/MainCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import static de.erethon.xlib.chat.FatLetter.*; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.plugin.PluginManager; /** * @author Daniel Saukel */ public class MainCommand extends DCommand { public MainCommand(DungeonsXL plugin) { super(plugin); setCommand("main"); setHelp(DMessage.CMD_MAIN_HELP.getMessage()); setPermission(DPermission.MAIN.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { PluginManager plugins = Bukkit.getServer().getPluginManager(); int maps = DungeonsXL.MAPS.listFiles().length - 1; int dungeons = DungeonsXL.DUNGEONS.listFiles().length; int loaded = plugin.getInstanceCache().size(); int players = dPlayers.getAllInstancePlayers().size(); String internals = Version.get().getRelocationTarget(); String vault = ""; if (plugins.getPlugin("Vault") != null) { vault = plugins.getPlugin("Vault").getDescription().getVersion(); } String xlib = plugins.getPlugin("XLib-Runtime").getDescription().getVersion(); MessageUtil.sendCenteredMessage(sender, "&4" + D[0] + "&f" + X[0] + L[0]); MessageUtil.sendCenteredMessage(sender, "&4" + D[1] + "&f" + X[1] + L[1]); MessageUtil.sendCenteredMessage(sender, "&4" + D[2] + "&f" + X[2] + L[2]); MessageUtil.sendCenteredMessage(sender, "&4" + D[3] + "&f" + X[3] + L[3]); MessageUtil.sendCenteredMessage(sender, "&4" + D[4] + "&f" + X[4] + L[4]); MessageUtil.sendCenteredMessage(sender, "&b&l###### " + DMessage.CMD_MAIN_WELCOME.getMessage() + "&7 v" + plugin.getDescription().getVersion() + " &b&l######"); MessageUtil.sendCenteredMessage(sender, DMessage.CMD_MAIN_LOADED.getMessage(String.valueOf(maps), String.valueOf(dungeons), String.valueOf(loaded), String.valueOf(players))); MessageUtil.sendCenteredMessage(sender, DMessage.CMD_MAIN_COMPATIBILITY.getMessage(internals, vault, xlib)); MessageUtil.sendCenteredMessage(sender, DMessage.CMD_MAIN_HELP_INFO.getMessage()); MessageUtil.sendCenteredMessage(sender, "&7\u00a92012-'13 Frank Baumann; '15-'26 Daniel Saukel & contributors."); MessageUtil.sendCenteredMessage(sender, "&7Licensed under GPLv3."); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/MsgCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.dungeonsxl.world.WorldConfig; import java.util.HashMap; import java.util.Map; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class MsgCommand extends DCommand { public MsgCommand(DungeonsXL plugin) { super(plugin); setMinArgs(-1); setMaxArgs(-1); setCommand("message"); setAliases("msg"); setHelp(DMessage.CMD_MSG_HELP.getMessage()); setPermission(DPermission.MESSAGE.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; EditWorld editWorld = plugin.getEditWorld(player.getWorld()); if (editWorld == null) { MessageUtil.sendMessage(player, DMessage.ERROR_NOT_IN_DUNGEON.getMessage()); return; } if (args.length <= 1) { displayHelp(player); return; } try { int id = Integer.parseInt(args[1]); WorldConfig config = ((DResourceWorld) editWorld.getResource()).getConfig(true); Map msgs = config.getState(GameRule.MESSAGES); if (msgs == null) { config.setState(GameRule.MESSAGES, new HashMap<>()); msgs = config.getState(GameRule.MESSAGES); } if (args.length == 2) { String msg = msgs.get(id); if (msg != null) { MessageUtil.sendMessage(player, ChatColor.WHITE + msg); } else { MessageUtil.sendMessage(player, DMessage.ERROR_MSG_ID_NOT_EXIST.getMessage(String.valueOf(id))); } } else { String msg = ""; int i = 0; for (String arg : args) { i++; if (i > 2) { msg = msg + " " + arg; } } String[] splitMsg = msg.split("\""); if (splitMsg.length > 1) { msg = splitMsg[1]; String old = msgs.get(id); if (old == null) { MessageUtil.sendMessage(player, DMessage.CMD_MSG_ADDED.getMessage(String.valueOf(id))); } else { MessageUtil.sendMessage(player, DMessage.CMD_MSG_UPDATED.getMessage(String.valueOf(id))); } msgs.put(id, msg); config.save(); for (Dungeon dungeon : plugin.getDungeonRegistry()) { if (!dungeon.getStartFloor().equals(editWorld.getResource())) { continue; } // Only MFD overrideValues can override floor configs if (dungeon.isMultiFloor()) { Map overrideValuesMSG = dungeon.getOverrideValues().getState(GameRule.MESSAGES); if (overrideValuesMSG != null && overrideValuesMSG.containsKey(id)) { continue; } } dungeon.getRules().getState(GameRule.MESSAGES).put(id, msg); } } else { MessageUtil.sendMessage(player, DMessage.ERROR_MSG_FORMAT.getMessage()); } } } catch (NumberFormatException e) { MessageUtil.sendMessage(player, DMessage.ERROR_MSG_NO_INT.getMessage()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/PlayCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DInstancePlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class PlayCommand extends DCommand { public PlayCommand(DungeonsXL plugin) { super(plugin); setCommand("play"); setMinArgs(1); setMaxArgs(1); setHelp(DMessage.CMD_PLAY_HELP.getMessage()); setPermission(DPermission.PLAY.getNode()); setPlayerCommand(true); setConsoleCommand(false); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; GlobalPlayer dPlayer = dPlayers.get(player); if (dPlayer instanceof DInstancePlayer) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); return; } Dungeon dungeon = plugin.getDungeonRegistry().get(args[1]); if (dungeon == null) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_SUCH_DUNGEON.getMessage(args[1])); return; } DGroup group = (DGroup) dPlayer.getGroup(); if (group != null && group.isPlaying()) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_GROUP.getMessage()); return; } else if (group == null) { group = DGroup.create(plugin, GroupCreateEvent.Cause.COMMAND, player, null, null, dungeon); if (group == null) { return; } } if (!group.getLeader().equals(player) && !DPermission.hasPermission(player, DPermission.BYPASS)) { MessageUtil.sendMessage(player, DMessage.ERROR_NOT_LEADER.getMessage()); return; } group.setDungeon(dungeon); if (!dPlayer.checkRequirements(dungeon)) { return; } Game game = new DGame(plugin, dungeon, group); GameWorld gameWorld = game.ensureWorldIsLoaded(false); if (gameWorld == null) { MessageUtil.sendMessage(player, DMessage.ERROR_TOO_MANY_INSTANCES.getMessage()); return; } for (Player groupPlayer : group.getMembers().getOnlinePlayers()) { new DGamePlayer(plugin, groupPlayer, group.getGameWorld()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/PortalCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.xlib.XLib; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.global.DPortal; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class PortalCommand extends DCommand { XLib xlib = plugin.getXLib(); public PortalCommand(DungeonsXL plugin) { super(plugin); setCommand("portal"); setMinArgs(0); setMaxArgs(2); setHelp(DMessage.CMD_PORTAL_HELP.getMessage()); setPermission(DPermission.PORTAL.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; DGlobalPlayer dGlobalPlayer = (DGlobalPlayer) dPlayers.get(player); if (dGlobalPlayer instanceof DGamePlayer) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); return; } ExItem material = null; if (args.length == 2) { material = xlib.getExItem(args[1]); } if (material == null) { material = VanillaItem.NETHER_PORTAL; } DPortal dPortal = dGlobalPlayer.getPortal(); if (dPortal == null) { dPortal = new DPortal(plugin, plugin.getGlobalProtectionCache().generateId(DPortal.class, player.getWorld()), player.getWorld(), material, false); plugin.getGlobalProtectionCache().addProtection(dPortal); dGlobalPlayer.setCreatingPortal(dPortal); dGlobalPlayer.setCachedItem(player.getInventory().getItemInHand()); player.getInventory().setItemInHand(VanillaItem.WOODEN_SWORD.toItemStack()); MessageUtil.sendMessage(player, DMessage.PLAYER_PORTAL_INTRODUCTION.getMessage()); } else { if (args.length == 3 && VanillaItem.NETHER_PORTAL.getId().equalsIgnoreCase(args[1])) { if (args[2].equalsIgnoreCase("-rotate")) { dPortal.rotate(); } dGlobalPlayer.setCreatingPortal(null); MessageUtil.sendMessage(player, DMessage.PLAYER_PORTAL_CREATED.getMessage()); return; } dPortal.delete(); dGlobalPlayer.setCreatingPortal(null); player.getInventory().setItemInHand(dGlobalPlayer.getCachedItem()); dGlobalPlayer.setCachedItem(null); MessageUtil.sendMessage(player, DMessage.PLAYER_PORTAL_ABORT.getMessage()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/ReloadCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.DataReloadEvent; import de.erethon.dungeonsxl.api.player.GroupAdapter; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.DefaultFontInfo; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import java.util.Collection; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; /** * @author Frank Baumann, Daniel Saukel */ public class ReloadCommand extends DCommand { public ReloadCommand(DungeonsXL plugin) { super(plugin); setCommand("reload"); setMinArgs(0); setMaxArgs(1); setHelp(DMessage.CMD_RELOAD_HELP.getMessage()); setPermission(DPermission.RELOAD.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { if (plugin.isLoadingWorld()) { MessageUtil.sendMessage(sender, DMessage.CMD_RELOAD_FAIL.getMessage()); return; } Collection dPlayers = this.dPlayers.getAllInstancePlayers(); if (!dPlayers.isEmpty() && args.length == 1 && sender instanceof Player) { MessageUtil.sendMessage(sender, DMessage.CMD_RELOAD_PLAYERS.getMessage()); ClickEvent onClick = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/dungeonsxl reload -force"); String message = DefaultFontInfo.getCenterSpaces(DMessage.BUTTON_OKAY.getMessage()) + DMessage.BUTTON_OKAY.getMessage(); TextComponent text = new TextComponent(message); text.setClickEvent(onClick); ((Player) sender).spigot().sendMessage(text); return; } PluginManager plugins = Bukkit.getPluginManager(); DataReloadEvent event = new DataReloadEvent(); plugins.callEvent(event); if (event.isCancelled()) { return; } dPlayers.forEach(InstancePlayer::leave); int maps = DungeonsXL.MAPS.listFiles().length - 1; int dungeons = DungeonsXL.DUNGEONS.listFiles().length; int loaded = plugin.getInstanceCache().size(); int players = this.dPlayers.getAllGamePlayers().size(); String internals = Version.get().getRelocationTarget(); String vault = ""; if (plugins.getPlugin("Vault") != null) { vault = plugins.getPlugin("Vault").getDescription().getVersion(); } String xlib = plugins.getPlugin("XLib-Runtime").getDescription().getVersion(); plugin.saveData(); plugin.initFolders(); plugin.reload(); plugin.checkState(); plugin.getGroupAdapters().forEach(GroupAdapter::clear); MessageUtil.sendPluginTag(sender, plugin); MessageUtil.sendCenteredMessage(sender, DMessage.CMD_RELOAD_SUCCESS.getMessage()); MessageUtil.sendCenteredMessage(sender, DMessage.CMD_MAIN_LOADED.getMessage(String.valueOf(maps), String.valueOf(dungeons), String.valueOf(loaded), String.valueOf(players))); MessageUtil.sendCenteredMessage(sender, DMessage.CMD_MAIN_COMPATIBILITY.getMessage(internals, vault, xlib)); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/RenameCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DDungeon; import de.erethon.dungeonsxl.dungeon.DungeonConfig; import de.erethon.dungeonsxl.global.GlobalProtection; import de.erethon.dungeonsxl.global.JoinSign; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.xlib.chat.MessageUtil; import java.io.File; import java.io.IOException; import java.util.List; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; /** * @author Daniel Saukel */ public class RenameCommand extends DCommand { public RenameCommand(DungeonsXL plugin) { super(plugin); setCommand("rename"); setMinArgs(2); setMaxArgs(2); setHelp(DMessage.CMD_RENAME_HELP.getMessage()); setPermission(DPermission.RENAME.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { DResourceWorld resource = (DResourceWorld) plugin.getMapRegistry().get(args[1]); if (resource == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_MAP.getMessage(args[1])); return; } Dungeon sfd = resource.getSingleFloorDungeon(); resource.setName(args[2]); resource.getFolder().renameTo(new File(DungeonsXL.MAPS, args[2])); resource.getSignData().updateFile(resource); if (resource.getEditWorld() != null) { resource.getEditWorld().delete(true); } for (Dungeon dungeon : plugin.getDungeonRegistry()) { if (!dungeon.isMultiFloor()) { continue; } DungeonConfig dConfig = ((DDungeon) dungeon).getConfig(); FileConfiguration config = dConfig.getConfig(); File file = dConfig.getFile(); if (dConfig.getStartFloor() == resource) { config.set("startFloor", args[2]); } if (dConfig.getEndFloor() == resource) { config.set("endFloor", args[2]); } List list = config.getStringList("floors"); int i = 0; for (ResourceWorld floor : dConfig.getFloors()) { if (floor == resource) { list.set(i, args[2]); } i++; } config.set("floors", list); try { config.save(file); } catch (IOException ex) { } } sfd.setName(args[2]); plugin.getDungeonRegistry().removeKey(args[1]); plugin.getDungeonRegistry().add(args[2], sfd); plugin.getMapRegistry().removeKey(args[1]); plugin.getMapRegistry().add(args[2], resource); boolean changed = false; for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections().toArray(GlobalProtection[]::new)) { if (!(protection instanceof JoinSign)) { continue; } Dungeon dungeon = ((JoinSign) protection).getDungeon(); if (dungeon == null) { protection.delete(); continue; } if (dungeon.getName().equals(args[1])) { dungeon.setName(args[2]); changed = true; } } if (changed) { plugin.getGlobalProtectionCache().saveAll(); } MessageUtil.sendMessage(sender, DMessage.CMD_RENAME_SUCCESS.getMessage(args[1], args[2])); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/ResourcePackCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class ResourcePackCommand extends DCommand { public ResourcePackCommand(DungeonsXL plugin) { super(plugin); setCommand("resourcepack"); setMinArgs(1); setMaxArgs(1); setHelp(DMessage.CMD_RESOURCE_PACK_HELP.getMessage()); setPermission(DPermission.RESOURCE_PACK.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; if (args[1].equalsIgnoreCase("reset")) { // Placeholder to reset to default player.setResourcePack("http://google.com"); return; } String url = (String) config.getResourcePacks().get(args[1]); if (url == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_RESOURCE_PACK.getMessage(args[1])); return; } player.setResourcePack(url); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/SaveCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.config.MainConfig.BackupMode; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class SaveCommand extends DCommand { public SaveCommand(DungeonsXL plugin) { super(plugin); setCommand("save"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_SAVE_HELP.getMessage()); setPermission(DPermission.SAVE.getNode()); setPlayerCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; EditWorld editWorld = plugin.getEditWorld(player.getWorld()); if (editWorld != null) { BackupMode backupMode = config.getBackupMode(); if (backupMode == BackupMode.ON_SAVE || backupMode == BackupMode.ON_DISABLE_AND_SAVE) { editWorld.getResource().backup(); } editWorld.save(); MessageUtil.sendMessage(player, DMessage.CMD_SAVE_SUCCESS.getMessage()); } else { MessageUtil.sendMessage(player, DMessage.ERROR_NOT_IN_DUNGEON.getMessage()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/StatusCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.DependencyVersion; import static de.erethon.dungeonsxl.util.DependencyVersion.*; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class StatusCommand extends DCommand { public static final String TRUE = ChatColor.GREEN + "\u2714"; public static final String FALSE = ChatColor.DARK_RED + "\u2718"; public StatusCommand(DungeonsXL plugin) { super(plugin); setCommand("status"); setMinArgs(0); setMaxArgs(0); setHelp(DMessage.CMD_STATUS_HELP.getMessage()); setPermission(DPermission.STATUS.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { String minecraftVersion = Version.get().toString(); String bukkitVersion = Bukkit.getBukkitVersion(); String dungeonsxlVersion = plugin.getDescription().getVersion(); boolean atLeastMin = Version.isAtLeast(DependencyVersion.META.getMinVersion()); boolean atMostMax = Version.isAtMost(Version.values()[Version.values().length - 2]); String versionCorrect = getSymbol(atLeastMin && atMostMax); if (atLeastMin && !atMostMax) { versionCorrect += "(possibly works through forward compatibility)"; } String dungeonsxlVersionCorrect = getSymbol(!dungeonsxlVersion.contains("SNAPSHOT")); MessageUtil.sendCenteredMessage(sender, "&4&l=> &6STATUS &4&l<="); MessageUtil.sendMessage(sender, ChatColor.GRAY + "Version info:"); MessageUtil.sendMessage(sender, "= Minecraft: " + minecraftVersion + " " + versionCorrect); MessageUtil.sendMessage(sender, "= Bukkit: " + bukkitVersion + " " + versionCorrect); MessageUtil.sendMessage(sender, "= DungeonsXL: " + dungeonsxlVersion + " " + dungeonsxlVersionCorrect); String permissionPlugin = "No plugin found"; String economyPlugin = "No plugin found"; if (VAULT.isEnabled()) { if (xlib.getPermissionProvider() != null) { permissionPlugin = xlib.getPermissionProvider().getName(); } if (xlib.getEconomyProvider() != null) { economyPlugin = xlib.getEconomyProvider().getName(); } } String permissionPluginCorrect = getSymbol(xlib.getPermissionProvider() != null && xlib.getPermissionProvider().hasGroupSupport()); String economyPluginCorrect = getSymbol(!plugin.getMainConfig().isEconomyEnabled() || xlib.getEconomyProvider() != null); MessageUtil.sendMessage(sender, ChatColor.GRAY + "Dependency info:"); for (DependencyVersion dependency : DependencyVersion.values()) { if (sender instanceof Player) { ((Player) sender).spigot().sendMessage(statusMsg(dependency)); } else { MessageUtil.sendMessage(sender, msgText(dependency)); } if (dependency == VAULT) { MessageUtil.sendMessage(sender, " = Permissions: " + permissionPlugin + " " + permissionPluginCorrect); MessageUtil.sendMessage(sender, " = Economy: " + economyPlugin + " " + economyPluginCorrect); } } } private static String msgText(DependencyVersion dependency) { return "= " + dependency.getName() + ": " + dependency.getEnabledVersion() + " " + getSymbol(dependency.check()); } private static BaseComponent statusMsg(DependencyVersion dependency) { TextComponent text = new TextComponent(msgText(dependency)); if (!dependency.check()) { HoverEvent event = new HoverEvent( HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("The tested version is: ").color(ChatColor.GRAY) .append(dependency.getSupportedVersion()).color(ChatColor.GREEN).create()); text.setHoverEvent(event); } return text; } private static String getSymbol(boolean value) { return value ? TRUE : FALSE; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/TestCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DInstancePlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.config.CommonMessage; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class TestCommand extends DCommand { public TestCommand(DungeonsXL plugin) { super(plugin); setCommand("test"); setMinArgs(1); setMaxArgs(1); setHelp(DMessage.CMD_TEST_HELP.getMessage()); setPlayerCommand(true); setConsoleCommand(false); } @Override public void onExecute(String[] args, CommandSender sender) { Player player = (Player) sender; GlobalPlayer dPlayer = dPlayers.get(player); if (dPlayer instanceof DInstancePlayer) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_DUNGEON.getMessage()); return; } Dungeon dungeon = plugin.getDungeonRegistry().get(args[1]); if (dungeon == null) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_SUCH_DUNGEON.getMessage(args[1])); return; } if (!dungeon.getMap().isInvitedPlayer(player) && !DPermission.hasPermission(player, DPermission.TEST)) { MessageUtil.sendMessage(player, CommonMessage.CMD_NO_PERMISSION.getMessage()); return; } DGroup group = (DGroup) dPlayer.getGroup(); if (group != null && group.isPlaying()) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_GROUP.getMessage()); return; } else if (group == null) { group = DGroup.create(plugin, GroupCreateEvent.Cause.COMMAND, player, null, null, dungeon); if (group == null) { return; } } if (!group.getLeader().equals(player) && !DPermission.hasPermission(player, DPermission.BYPASS)) { MessageUtil.sendMessage(player, DMessage.ERROR_NOT_LEADER.getMessage()); return; } group.setDungeon(dungeon); if (!dPlayer.checkRequirements(dungeon)) { return; } Game game = new DGame(plugin, dungeon, group); game.setRewards(false); GameWorld gameWorld = game.ensureWorldIsLoaded(false); if (gameWorld == null) { MessageUtil.sendMessage(player, DMessage.ERROR_TOO_MANY_INSTANCES.getMessage()); return; } for (Player groupPlayer : group.getMembers().getOnlinePlayers()) { new DGamePlayer(plugin, groupPlayer, group.getGameWorld()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/command/UninviteCommand.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.command; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; /** * @author Frank Baumann, Daniel Saukel */ public class UninviteCommand extends DCommand { public UninviteCommand(DungeonsXL plugin) { super(plugin); setCommand("uninvite"); setMinArgs(2); setMaxArgs(2); setHelp(DMessage.CMD_UNINVITE_HELP.getMessage()); setPermission(DPermission.UNINVITE.getNode()); setPlayerCommand(true); setConsoleCommand(true); } @Override public void onExecute(String[] args, CommandSender sender) { ResourceWorld resource = plugin.getMapRegistry().get(args[2]); if (resource == null) { MessageUtil.sendMessage(sender, DMessage.ERROR_NO_SUCH_MAP.getMessage(args[2])); return; } OfflinePlayer player = Bukkit.getOfflinePlayer(args[1]); if (resource.removeInvitedPlayer(player)) { MessageUtil.sendMessage(sender, DMessage.CMD_UNINVITE_SUCCESS.getMessage(args[1], args[2])); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/config/DMessage.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.config; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.config.Message; import de.erethon.xlib.config.MessageHandler; /** * An enumeration of all messages. The values are fetched from the language file. * * @author Daniel Saukel */ public enum DMessage implements Message { ANNOUNCER_CLICK("announcer.click"), BUTTON_ACCEPT("button.accept"), BUTTON_DENY("button.deny"), BUTTON_OKAY("button.okay"), CMD_ANNOUNCE_HELP("cmd.announce.help"), CMD_BREAK_BREAK_MODE("cmd.break.breakMode"), CMD_BREAK_HELP("cmd.break.help"), CMD_BREAK_PROTECTED_MODE("cmd.break.protectedMode"), CMD_CHAT_HELP("cmd.chat.help"), CMD_CHAT_DUNGEON_CHAT("cmd.chat.dungeonChat"), CMD_CHAT_NORMAL_CHAT("cmd.chat.normalChat"), CMD_CHATSPY_HELP("cmd.chatspy.help"), CMD_CHATSPY_STOPPED("cmd.chatspy.stopped"), CMD_CHATSPY_STARTED("cmd.chatspy.started"), CMD_CREATE_HELP("cmd.create.help"), CMD_DELETE_BACKUPS("cmd.delete.backups"), CMD_DELETE_HELP("cmd.delete.help"), CMD_DELETE_SUCCESS("cmd.delete.success"), CMD_DUNGEON_ITEM_HELP("cmd.dungeonItem.help"), CMD_DUNGEON_ITEM_DUNGEON_ITEM_HELP("cmd.dungeonItem.dungeonItemHelp"), CMD_DUNGEON_ITEM_GLOBAL_ITEM_HELP("cmd.dungeonItem.globalItemHelp"), CMD_DUNGEON_ITEM_INFO_DUNGEON("cmd.dungeonItem.info.dungeon"), CMD_DUNGEON_ITEM_INFO_GLOBAL("cmd.dungeonItem.info.global"), CMD_DUNGEON_ITEM_SET_DUNGEON("cmd.dungeonItem.set.dungeon"), CMD_DUNGEON_ITEM_SET_GLOBAL("cmd.dungeonItem.set.global"), CMD_EDIT_HELP("cmd.edit.help"), CMD_ENTER_HELP("cmd.enter.help"), CMD_ENTER_SUCCESS("cmd.enter.success"), CMD_ESCAPE_HELP("cmd.escape.help"), CMD_GAME_HELP("cmd.game.help"), CMD_GROUP_HELP_MAIN("cmd.group.help.main"), CMD_GROUP_HELP_CREATE("cmd.group.help.create"), CMD_GROUP_HELP_DISBAND("cmd.group.help.disband"), CMD_GROUP_HELP_INVITE("cmd.group.help.invite"), CMD_GROUP_HELP_JOIN("cmd.group.help.join"), CMD_GROUP_HELP_KICK("cmd.group.help.kick"), CMD_GROUP_HELP_SHOW("cmd.group.help.show"), CMD_GROUP_HELP_UNINVITE("cmd.group.help.uninvite"), CMD_HELP_HELP("cmd.help.help"), CMD_IMPORT_HELP("cmd.import.help"), CMD_IMPORT_SUCCESS("cmd.import.success"), CMD_INVITE_HELP("cmd.invite.help"), CMD_INVITE_SUCCESS("cmd.invite.success"), CMD_JOIN_HELP("cmd.join.help"), CMD_KICK_HELP("cmd.kick.help"), CMD_KICK_SUCCESS("cmd.kick.success"), CMD_LEAVE_HELP("cmd.leave.help"), CMD_LEAVE_SUCCESS("cmd.leave.success"), CMD_LIST_HELP("cmd.list.help"), CMD_LIVES_GROUP("cmd.lives.group"), CMD_LIVES_HELP("cmd.lives.help"), CMD_LIVES_PLAYER("cmd.lives.player"), CMD_MAIN_WELCOME("cmd.main.welcome"), CMD_MAIN_LOADED("cmd.main.loaded"), CMD_MAIN_COMPATIBILITY("cmd.main.compatibility"), CMD_MAIN_HELP("cmd.main.help"), CMD_MAIN_HELP_INFO("cmd.main.helpInfo"), CMD_MSG_ADDED("cmd.msg.added"), CMD_MSG_HELP("cmd.msg.help"), CMD_MSG_UPDATED("cmd.msg.updated"), CMD_PORTAL_HELP("cmd.portal.help"), CMD_PLAY_HELP("cmd.play.help"), CMD_RELOAD_FAIL("cmd.reload.fail"), CMD_RELOAD_HELP("cmd.reload.help"), CMD_RELOAD_SUCCESS("cmd.reload.success"), CMD_RELOAD_PLAYERS("cmd.reload.players"), CMD_RENAME_HELP("cmd.rename.help"), CMD_RENAME_SUCCESS("cmd.rename.success"), CMD_RESOURCE_PACK_HELP("cmd.resourcePack.help"), CMD_SAVE_HELP("cmd.save.help"), CMD_SAVE_SUCCESS("cmd.save.success"), CMD_STATUS_HELP("cmd.status.help"), CMD_TEST_HELP("cmd.test.help"), CMD_UNINVITE_HELP("cmd.uninvite.help"), CMD_UNINVITE_SUCCESS("cmd.uninvite.success"), DAY_OF_WEEK_0("dayOfWeek.0"), DAY_OF_WEEK_1("dayOfWeek.1"), DAY_OF_WEEK_2("dayOfWeek.2"), DAY_OF_WEEK_3("dayOfWeek.3"), DAY_OF_WEEK_4("dayOfWeek.4"), DAY_OF_WEEK_5("dayOfWeek.5"), DAY_OF_WEEK_6("dayOfWeek.6"), ERROR_BED("error.bed"), ERROR_CHEST_IS_OPENED("error.chestIsOpened"), ERROR_CMD("error.cmd"), ERROR_DISPENSER("error.dispenser"), ERROR_DROP("error.drop"), ERROR_ENDERCHEST("error.enderchest"), ERROR_GROUP_IS_PLAYING("error.groupIsPlaying"), ERROR_IN_GROUP("error.inGroup"), ERROR_JOIN_GROUP("error.joinGroup"), ERROR_LEAVE_DUNGEON("error.leaveDungeon"), ERROR_LEAVE_GAME("error.leaveGame"), ERROR_LEAVE_GROUP("error.leaveGroup"), ERROR_MSG_ID_NOT_EXIST("error.msgIdDoesNotExist"), ERROR_MSG_FORMAT("error.msgFormat"), ERROR_MSG_NO_INT("error.msgNoInt"), ERROR_NAME_IN_USE("error.nameInUse"), ERROR_NAME_TOO_LONG("error.nameTooLong"), ERROR_NO_GAME("error.noGame"), ERROR_NO_ITEM_IN_MAIN_HAND("error.noItemInMainHand"), ERROR_NO_LEAVE_IN_TUTORIAL("error.noLeaveInTutorial"), ERROR_NO_PERMISSIONS("error.noPermissions"), ERROR_NO_PROTECTED_BLOCK("error.noProtectedBlock"), ERROR_NO_READY_SIGN("error.noReadySign"), ERROR_NO_REWARDS_TIME("error.noRewardsTime"), ERROR_NO_SUCH_ANNOUNCER("error.noSuchAnnouncer"), ERROR_NO_SUCH_DUNGEON("error.noSuchDungeon"), ERROR_NO_SUCH_GROUP("error.noSuchGroup"), ERROR_NO_SUCH_MAP("error.noSuchMap"), ERROR_NO_SUCH_PLAYER("error.noSuchPlayer"), ERROR_NO_SUCH_RESOURCE_PACK("error.noSuchResourcePack"), ERROR_NO_SUCH_SHOP("error.noSuchShop"), ERROR_NOT_IN_DUNGEON("error.notInDungeon"), ERROR_NOT_IN_GAME("error.notInGame"), ERROR_NOT_IN_GROUP("error.notInGroup"), ERROR_NOT_INVITED("error.notInvited"), ERROR_NOT_LEADER("error.notLeader"), ERROR_NOT_SAVED("error.notSaved"), ERROR_BLOCK_OWN_TEAM("error.blockOwnTeam"), ERROR_READY("error.ready"), ERROR_REQUIREMENTS("error.requirements"), ERROR_SELF_NOT_IN_GROUP("error.selfNotInGroup"), ERROR_SIGN_WRONG_FORMAT("error.signWrongFormat"), ERROR_TOO_MANY_INSTANCES("error.tooManyInstances"), ERROR_TOO_MANY_TUTORIALS("error.tooManyTutorials"), ERROR_TUTORIAL_DOES_NOT_EXIST("error.tutorialDoesNotExist"), GROUP_BED_DESTROYED("group.bedDestroyed"), GROUP_CONGRATS("group.congrats"), GROUP_CONGRATS_SUB("group.congratsSub"), GROUP_CREATED("group.created"), GROUP_DEATH("group.death"), GROUP_DEATH_KICK("group.deathKick"), GROUP_DEFEATED("group.defeated"), GROUP_DISBANDED("group.disbanded"), GROUP_FLAG_CAPTURED("group.flagCaptured"), GROUP_FLAG_LOST("group.flagLost"), GROUP_FLAG_STEALING("group.flagStealing"), GROUP_INVITED_PLAYER("group.invitedPlayer"), GROUP_JOINED_GAME("group.joinedGame"), GROUP_KILLED("group.killed"), GROUP_KILLED_KICK("group.killedKick"), GROUP_LIVES_ADDED("group.livesAdded"), GROUP_LIVES_REMOVED("group.livesRemoved"), GROUP_KICKED_PLAYER("group.kickedPlayer"), GROUP_PLAYER_JOINED("group.playerJoined"), GROUP_REWARD_CHEST("group.rewardChest"), GROUP_UNINVITED_PLAYER("group.uninvitedPlayer"), GROUP_WAVE_FINISHED("group.waveFinished"), PLAYER_BLOCK_INFO("player.blockInfo"), PLAYER_CHECKPOINT_REACHED("player.checkpointReached"), PLAYER_DEATH("player.death"), PLAYER_DEATH_KICK("player.deathKick"), PLAYER_FINISHED_DUNGEON("player.finishedDungeon"), PLAYER_FINISHED_FLOOR("player.finished_Floor"), PLAYER_INVITED("player.invited"), PLAYER_UNINVITED("player.uninvited"), PLAYER_JOIN_GROUP("player.joinGroup"), PLAYER_KICKED("player.kicked"), PLAYER_KILLED("player.killed"), PLAYER_KILLED_KICK("player.killedKick"), PLAYER_LEAVE_GROUP("player.leaveGroup"), PLAYER_LEFT_GROUP("player.leftGroup"), PLAYER_LIVES_ADDED("player.livesAdded"), PLAYER_LIVES_REMOVED("player.livesRemoved"), PLAYER_LOOT_ADDED("player.lootAdded"), PLAYER_NEW_LEADER("player.newLeader"), PLAYER_OFFLINE("player.offline"), PLAYER_OFFLINE_NEVER("player.offlineNever"), PLAYER_PORTAL_ABORT("player.portal.abort"), PLAYER_PORTAL_INTRODUCTION("player.portal.introduction"), PLAYER_PORTAL_CREATED("player.portal.created"), PLAYER_PORTAL_PROGRESS("player.portal.progress"), PLAYER_PORTAL_ROTATE("player.portal.rotate"), PLAYER_PROTECTED_BLOCK_DELETED("player.protectedBlockDeleted"), PLAYER_READY("player.ready"), PLAYER_SIGN_CREATED("player.signCreated"), PLAYER_SIGN_COPIED("player.signCopied"), PLAYER_TIME_LEFT("player.timeLeft"), PLAYER_TIME_KICK("player.timeKick"), PLAYER_TREASURES("player.treasures"), PLAYER_UNLIMITED_LIVES("player.unlimitedLives"), PLAYER_WAIT_FOR_OTHER_PLAYERS("player.waitForOtherPlayers"), REQUIREMENT_FEE("requirement.fee"), REQUIREMENT_FEE_ITEMS("requirement.feeItems"), REQUIREMENT_FEE_LEVEL("requirement.feeLevel"), REQUIREMENT_FEE_MONEY("requirement.feeMoney"), REQUIREMENT_FINISHED_DUNGEONS_AND("requirement.finishedDungeons.and"), REQUIREMENT_FINISHED_DUNGEONS_NAME("requirement.finishedDungeons.name"), REQUIREMENT_FINISHED_DUNGEONS_OR("requirement.finishedDungeons.or"), REQUIREMENT_FINISHED_DUNGEONS_WITHIN_TIME("requirement.finishedDungeons.withinTime"), REQUIREMENT_FORBIDDEN_ITEMS("requirement.forbiddenItems"), REQUIREMENT_GROUP_SIZE("requirement.groupSize"), REQUIREMENT_KEY_ITEMS("requirement.keyItems"), REQUIREMENT_PERMISSION("requirement.permission"), REQUIREMENT_TIME_SINCE_NEVER("requirement.timeSince.never"), REQUIREMENT_TIME_SINCE_FINISH("requirement.timeSince.finish"), REQUIREMENT_TIME_SINCE_START("requirement.timeSince.start"), REQUIREMENT_TIMEFRAME("requirement.timeframe"), REWARD_GENERAL("reward.general"), SIGN_END("sign.end"), SIGN_FLOOR_1("sign.floor.1"), SIGN_FLOOR_2("sign.floor.2"), SIGN_GLOBAL_FULL("sign.global.full"), SIGN_GLOBAL_IS_PLAYING("sign.global.isPlaying"), SIGN_GLOBAL_JOIN_GAME("sign.global.joinGame"), SIGN_GLOBAL_JOIN_GROUP("sign.global.joinGroup"), SIGN_GLOBAL_NEW_GAME("sign.global.newGame"), SIGN_GLOBAL_NEW_GROUP("sign.global.newGroup"), SIGN_LEAVE("sign.leave"), SIGN_READY("sign.ready"), SIGN_RESOURCE_PACK("sign.resourcePack"), SIGN_WAVE_1("sign.wave.1"), SIGN_WAVE_2("sign.wave.2"); private String path; DMessage(String path) { this.path = path; } @Override public String getPath() { return path; } @Override public MessageHandler getMessageHandler() { return DungeonsXL.getInstance().getInitializer().getMessageHandler(); } @Override public void debug() { MessageUtil.log(DungeonsXL.getInstance(), getMessage()); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/config/MainConfig.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.config; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.player.PlayerGroup.Color; import static de.erethon.dungeonsxl.api.player.PlayerGroup.Color.*; import de.erethon.dungeonsxl.world.WorldConfig; import de.erethon.xlib.config.DREConfig; import de.erethon.xlib.util.EnumUtil; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.bukkit.configuration.ConfigurationSection; /** * Represents the main config.yml. * * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class MainConfig extends DREConfig { private DungeonsXL plugin; public enum BackupMode { ON_DISABLE, ON_DISABLE_AND_SAVE, ON_SAVE, NEVER } public static final int CONFIG_VERSION = 21; private String language = "english"; private boolean updaterEnabled = true; private boolean enableEconomy = false; private boolean groupAdaptersEnabled = false; /* Chat */ private boolean chatEnabled = true; private String chatFormatEdit = "&2[Edit] &r%player_name%: "; private String chatFormatGame = "&2[Game] %group_color%%player_name%: &r"; private String chatFormatGroup = "&2%group_color%[%group_name%] %player_name%: &r"; private String chatFormatSpy = "&2[Chat Spy] %player_name%: &r"; /* Tutorial */ private boolean tutorialActivated = false; private String tutorialDungeonName = "tutorial"; private Dungeon tutorialDungeon; private String tutorialStartGroup = "default"; private String tutorialEndGroup = "player"; /* Announcers */ private List groupColorPriority = new ArrayList<>(Arrays.asList( DARK_BLUE, LIGHT_RED, YELLOW, LIGHT_GREEN, PURPLE, ORANGE, BLACK, LIGHT_BLUE, DARK_GREEN, DARK_RED, LIGHT_GRAY, CYAN, MAGENTA, DARK_GRAY, PINK )); private double announcementInterval = 30; /* Misc */ private boolean sendFloorTitle = true; private boolean globalDeathMessagesDisabled = true; private Map externalMobProviders = new HashMap<>(); private Map resourcePacks = new HashMap<>(); /* Performance */ private int maxInstances = 10; private int editInstanceRemovalDelay = 5; private boolean strictMovementCheckEnabled = true; /* Secure Mode */ private boolean secureModeEnabled = false; private double secureModeCheckInterval = 5; private boolean openInventories = false; private boolean dropItems = false; private List editCommandWhitelist; private BackupMode backupMode = BackupMode.ON_DISABLE_AND_SAVE; private boolean lobbyContainersEnabled = false; /* Permissions bridge */ private List editPermissions; private WorldConfig defaultWorldConfig; public MainConfig(DungeonsXL plugin, File file) { super(file, CONFIG_VERSION); this.plugin = plugin; if (initialize) { initialize(); } load(); } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public boolean isUpdaterEnabled() { return updaterEnabled; } public void setUpdaterEnabled(boolean enabled) { updaterEnabled = enabled; } public boolean isEconomyEnabled() { return enableEconomy; } public void setEconomyEnabled(boolean enabled) { enableEconomy = enabled; } public boolean areGroupAdaptersEnabled() { return groupAdaptersEnabled; } public void setGroupAdaptersEnabled(boolean enabled) { groupAdaptersEnabled = enabled; } public boolean isChatEnabled() { return chatEnabled; } public void setChatEnabled(boolean enabled) { chatEnabled = enabled; } public String getChatFormatEdit() { return chatFormatEdit; } public void setEditFormatEdit(String string) { chatFormatEdit = string; } public String getChatFormatGame() { return chatFormatGame; } public void setChatFormatGame(String string) { chatFormatGame = string; } public String getChatFormatGroup() { return chatFormatGroup; } public void setChatFormatGroup(String string) { chatFormatGroup = string; } public String getChatFormatSpy() { return chatFormatSpy; } public void setChatFormatSpy(String string) { chatFormatSpy = string; } public boolean isTutorialActivated() { return tutorialActivated; } public void setTutorialActivated(boolean activated) { tutorialActivated = activated; } public Dungeon getTutorialDungeon() { if (tutorialDungeon == null) { tutorialDungeon = plugin.getDungeonRegistry().get(tutorialDungeonName); } return tutorialDungeon; } public void setTutorialDungeon(Dungeon dungeon) { tutorialDungeon = dungeon; } public String getTutorialStartGroup() { return tutorialStartGroup; } public void setTutorialStartGroup(String group) { tutorialStartGroup = group; } public String getTutorialEndGroup() { return tutorialEndGroup; } public void setTutorialEndGroup(String group) { tutorialEndGroup = group; } public List getGroupColorPriority() { return groupColorPriority; } public Color getGroupColorPriority(int count) { return (count < groupColorPriority.size() && count >= 0) ? groupColorPriority.get(count) : Color.WHITE; } public void setGroupColorPriority(List colors) { groupColorPriority = colors; } public long getAnnouncmentInterval() { return (long) (announcementInterval * 20); } public void setAnnouncementInterval(double interval) { announcementInterval = interval; } public boolean areGlobalDeathMessagesDisabled() { return globalDeathMessagesDisabled; } public void setGlobalDeathMessagesDisabled(boolean disabled) { globalDeathMessagesDisabled = false; } public boolean isSendFloorTitleEnabled() { return sendFloorTitle; } public void setSendFloorTitleEnabled(boolean enabled) { sendFloorTitle = enabled; } public Map getExternalMobProviders() { return externalMobProviders; } public Map getResourcePacks() { return resourcePacks; } public int getMaxInstances() { return maxInstances; } public void setMaxInstances(int maxInstances) { this.maxInstances = maxInstances; } public int getEditInstanceRemovalDelay() { return editInstanceRemovalDelay; } public void setEditInstanceRemovalDelay(int delay) { editInstanceRemovalDelay = delay; } public boolean isStrictMovementCheckEnabled() { return strictMovementCheckEnabled; } public void setStrictMovementCheckEnabled(boolean enabled) { strictMovementCheckEnabled = enabled; } public boolean isSecureModeEnabled() { return secureModeEnabled; } public void setSecureModeEnabled(boolean enabled) { secureModeEnabled = enabled; } public boolean getOpenInventories() { return openInventories && secureModeEnabled; } public void setOpenInventories(boolean openInventories) { this.openInventories = openInventories; } /** * @return if players may drop items while editing; false if secure mode disabled */ public boolean getDropItems() { return dropItems && secureModeEnabled; } /** * @param dropItems if items may be dropped in edit mode */ public void setDropItems(boolean dropItems) { this.dropItems = dropItems; } public long getSecureModeCheckInterval() { return (long) (secureModeCheckInterval * 20); } public void setSecureModeCheckInterval(double interval) { secureModeCheckInterval = interval; } public List getEditCommandWhitelist() { return editCommandWhitelist; } public BackupMode getBackupMode() { return backupMode; } public void setBackupMode(BackupMode mode) { backupMode = mode; } public boolean areLobbyContainersEnabled() { return lobbyContainersEnabled; } public void setLobbyContainersEnabled(boolean enabled) { lobbyContainersEnabled = enabled; } public List getEditPermissions() { return editPermissions; } public WorldConfig getDefaultWorldConfig() { return defaultWorldConfig; } @Override public void initialize() { /* Main Config */ if (!config.contains("language")) { config.set("language", language); } if (!config.contains("updaterEnabled")) { config.set("updaterEnabled", updaterEnabled); } if (!config.contains("enableEconomy")) { config.set("enableEconomy", enableEconomy); } if (!config.contains("groupAdaptersEnabled")) { config.set("groupAdaptersEnabled", groupAdaptersEnabled); } if (!config.contains("chatEnabled")) { config.set("chatEnabled", chatEnabled); } if (!config.contains("chatFormat.edit")) { config.set("chatFormat.edit", chatFormatEdit); } if (!config.contains("chatFormat.game")) { config.set("chatFormat.game", chatFormatGame); } if (!config.contains("chatFormat.group")) { config.set("chatFormat.group", chatFormatGroup); } if (!config.contains("chatFormat.spy")) { config.set("chatFormat.spy", chatFormatSpy); } if (!config.contains("tutorial.activated")) { config.set("tutorial.activated", tutorialActivated); } if (!config.contains("tutorial.dungeon")) { config.set("tutorial.dungeon", tutorialDungeonName); } if (!config.contains("tutorial.startGroup")) { config.set("tutorial.startGroup", tutorialStartGroup); } if (!config.contains("tutorial.endGroup")) { config.set("tutorial.endgroup", tutorialEndGroup); } if (!config.contains("groupColorPriority")) { ArrayList strings = new ArrayList<>(); for (Color color : groupColorPriority) { strings.add(color.toString()); } config.set("groupColorPriority", strings); } if (!config.contains("announcementInterval")) { config.set("announcementInterval", announcementInterval); } if (!config.contains("sendFloorTitle")) { config.set("sendFloorTitle", sendFloorTitle); } if (!config.contains("globalDeathMessagesDisabled")) { config.set("globalDeathMessagesDisabled", globalDeathMessagesDisabled); } if (!config.contains("externalMobProviders")) { config.createSection("externalMobProviders"); } if (!config.contains("resourcePacks")) { config.createSection("resourcePacks"); } if (!config.contains("maxInstances")) { config.set("maxInstances", maxInstances); } if (!config.contains("editInstanceRemovalDelay")) { config.set("editInstanceRemovalDelay", editInstanceRemovalDelay); } if (!config.contains("strictMovementCheckEnabled")) { config.set("strictMovementCheckEnabled", strictMovementCheckEnabled); } if (!config.contains("secureMode.enabled")) { config.set("secureMode.enabled", secureModeEnabled); } if (!config.contains("secureMode.openInventories")) { config.set("secureMode.openInventories", openInventories); } if (!config.contains("secureMode.dropItems")) { config.set("secureMode.dropItems", dropItems); } if (!config.contains("secureMode.checkInterval")) { config.set("secureMode.checkInterval", secureModeCheckInterval); } if (!config.contains("secureMode.editCommandWhitelist")) { config.set("secureMode.editCommandWhitelist", editCommandWhitelist); } if (!config.contains("backupMode")) { config.set("backupMode", backupMode.toString()); } if (!config.contains("lobbyContainersEnabled")) { config.set("lobbyContainersEnabled", lobbyContainersEnabled); } if (!config.contains("editPermissions")) { config.set("editPermissions", editPermissions); } /* Default Dungeon Config */ if (!config.contains("default")) { ConfigurationSection section = config.createSection("default"); section.set("damageProtectedEntities", Arrays.asList("ARMOR_STAND", "ITEM_FRAME", "PAINTING")); section.set("interactionProtectedEntities", Arrays.asList("ARMOR_STAND", "ITEM_FRAME")); } save(); } @Override public void load() { language = config.getString("language", language); plugin.getInitializer().getMessageHandler().setDefaultLanguage(language); updaterEnabled = config.getBoolean("updaterEnabled", updaterEnabled); enableEconomy = config.getBoolean("enableEconomy", enableEconomy); groupAdaptersEnabled = config.getBoolean("groupAdaptersEnabled", groupAdaptersEnabled); chatEnabled = config.getBoolean("chatEnabled", chatEnabled); chatFormatEdit = config.getString("chatFormat.edit", chatFormatEdit); chatFormatGame = config.getString("chatFormat.game", chatFormatGame); chatFormatGroup = config.getString("chatFormat.group", chatFormatGroup); chatFormatSpy = config.getString("chatFormat.spy", chatFormatSpy); chatEnabled = config.getBoolean("chatEnabled", chatEnabled); tutorialActivated = config.getBoolean("tutorial.activated", tutorialActivated); tutorialDungeonName = config.getString("tutorial.dungeon", tutorialDungeonName); tutorialStartGroup = config.getString("tutorial.startgroup", tutorialStartGroup); tutorialEndGroup = config.getString("tutorial.endgroup", tutorialEndGroup); if (config.getStringList("groupColorPriority").size() < 14) { ArrayList strings = new ArrayList<>(); for (Color color : groupColorPriority) { strings.add(color.toString()); } config.set("groupColorPriority", strings); try { config.save(file); } catch (IOException exception) { } } else { groupColorPriority.clear(); for (String color : config.getStringList("groupColorPriority")) { Color dColor = EnumUtil.getEnum(Color.class, color); if (dColor != null && dColor != Color.WHITE) { groupColorPriority.add(dColor); } } } announcementInterval = config.getDouble("announcementInterval", announcementInterval); sendFloorTitle = config.getBoolean("sendFloorTitle", sendFloorTitle); globalDeathMessagesDisabled = config.getBoolean("globalDeathMessagesDisabled", globalDeathMessagesDisabled); ConfigurationSection externalMobProvidersSection = config.getConfigurationSection("externalMobProviders"); if (externalMobProvidersSection != null) { externalMobProviders = externalMobProvidersSection.getValues(false); } ConfigurationSection resourcePacksSection = config.getConfigurationSection("resourcePacks"); if (resourcePacksSection != null) { resourcePacks = resourcePacksSection.getValues(false); } maxInstances = config.getInt("maxInstances", maxInstances); editInstanceRemovalDelay = config.getInt("editInstanceRemovalDelay", editInstanceRemovalDelay); strictMovementCheckEnabled = config.getBoolean("strictMovementCheckEnabled", strictMovementCheckEnabled); secureModeEnabled = config.getBoolean("secureMode.enabled", secureModeEnabled); openInventories = config.getBoolean("secureMode.openInventories", openInventories); dropItems = config.getBoolean("secureMode.dropItems", dropItems); secureModeCheckInterval = config.getDouble("secureMode.checkInterval", secureModeCheckInterval); editCommandWhitelist = config.getStringList("secureMode.editCommandWhitelist"); String mode = config.getString("backupMode"); if (EnumUtil.isValidEnum(BackupMode.class, mode)) { backupMode = BackupMode.valueOf(mode); } lobbyContainersEnabled = config.getBoolean("lobbyContainersEnabled", lobbyContainersEnabled); editPermissions = config.getStringList("editPermissions"); ConfigurationSection defaultWorldSection = config.getConfigurationSection("default"); if (defaultWorldSection != null) { defaultWorldConfig = new WorldConfig(plugin, defaultWorldSection); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/dungeon/DDungeon.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.dungeon; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.world.ResourceWorld; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** * @author Daniel Saukel */ public class DDungeon implements Dungeon { private DungeonsXL plugin; private String name; private DungeonConfig config; private ResourceWorld map; private GameRuleContainer rules; /** * Artificial dungeon * * @param plugin the plugin instance * @param resource the only resource world */ public DDungeon(DungeonsXL plugin, ResourceWorld resource) { this.plugin = plugin; name = resource.getName(); map = resource; setupRules(); } private DDungeon() { } /** * Real dungeon * * @param plugin the plugin instance * @param file the file to load from * @return the dungeon or null if the config is erroneous */ public static Dungeon create(DungeonsXL plugin, File file) { DungeonConfig config = new DungeonConfig(plugin, file); if (config.getStartFloor() == null || config.getEndFloor() == null) { return null; } DDungeon dungeon = new DDungeon(); dungeon.plugin = plugin; dungeon.name = file.getName().replaceAll(".yml", ""); dungeon.config = config; dungeon.map = config.getStartFloor(); if (dungeon.isSetupCorrect()) { dungeon.setupRules(); return dungeon; } else { return null; } } public DungeonConfig getConfig() { if (!isMultiFloor()) { throw new IllegalStateException("Tried to access the dungeon config of a single floor dungeon"); } return config; } @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } @Override public boolean isMultiFloor() { return config != null; } @Override public ResourceWorld getStartFloor() { return map; } @Override public void setStartFloor(ResourceWorld startFloor) { getConfig().setStartFloor(startFloor); } @Override public ResourceWorld getEndFloor() { return getConfig().getEndFloor(); } @Override public void setEndFloor(ResourceWorld endFloor) { getConfig().setEndFloor(endFloor); } @Override public List getFloors() { if (isMultiFloor()) { return new ArrayList<>(getConfig().getFloors()); } else { return new ArrayList<>(Arrays.asList(map)); } } @Override public void addFloor(ResourceWorld resource) { getConfig().addFloor(resource); } @Override public void removeFloor(ResourceWorld resource) { getConfig().removeFloor(resource); } @Override public int getFloorCount() { return getConfig().getFloorCount(); } @Override public void setFloorCount(int floorCount) { getConfig().setFloorCount(floorCount); } @Override public boolean getRemoveWhenPlayed() { return getConfig().getRemoveWhenPlayed(); } @Override public void setRemoveWhenPlayed(boolean removeWhenPlayed) { getConfig().setRemoveWhenPlayed(removeWhenPlayed); } @Override public GameRuleContainer getOverrideValues() { return getConfig().getOverrideValues(); } @Override public GameRuleContainer getDefaultValues() { return getConfig().getDefaultValues(); } @Override public GameRuleContainer getRules() { return rules; } @Override public void setRules(GameRuleContainer rules) { this.rules = rules; } @Override public void setupRules() { if (rules != null) { return; } if (isMultiFloor()) { rules = new GameRuleContainer(getOverrideValues()); if (map.getRules() != null) { rules.merge(map.getRules()); } rules.merge(getDefaultValues()); } else if (map.getRules() != null) { rules = new GameRuleContainer(map.getRules()); } else { rules = new GameRuleContainer(); } rules.merge(plugin.getMainConfig().getDefaultWorldConfig()); rules.merge(GameRule.DEFAULT_VALUES); } @Override public boolean isSetupCorrect() { for (ResourceWorld resource : plugin.getMapRegistry()) { if (resource.getName().equals(name)) { return false; } } return getConfig() == null || (getConfig().getStartFloor() != null && getConfig().getEndFloor() != null); } /* Statics */ public static File getFileFromName(String name) { return new File(DungeonsXL.DUNGEONS, name + ".yml"); } @Override public String toString() { return getClass().getSimpleName() + "{name=" + name + "; multiFloor=" + isMultiFloor() + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/dungeon/DGame.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.dungeon; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameGoal; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.global.GameSign; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.sign.windup.MobSign; import de.erethon.dungeonsxl.trigger.ProgressTrigger; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.xlib.chat.MessageUtil; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class DGame implements Game { private DungeonsXL plugin; private Dungeon dungeon; private GameWorld world; private List unplayedFloors = new ArrayList<>(); private ResourceWorld nextFloor; private int floorCount; private List groups = new ArrayList<>(); private boolean tutorial; private boolean rewards = true; private boolean started; private int waveCount; private Map gameKills = new HashMap<>(); private Map waveKills = new HashMap<>(); public DGame(DungeonsXL plugin, Dungeon dungeon) { this.plugin = plugin; plugin.getGameCache().add(this); setDungeon(dungeon); if (this.dungeon == null) { throw new IllegalStateException("Game initialized without dungeon"); } tutorial = false; started = false; } public DGame(DungeonsXL plugin, Dungeon dungeon, PlayerGroup group) { this(plugin, dungeon); addGroup(group); } public DGame(DungeonsXL plugin, Dungeon dungeon, List groups) { this(plugin, dungeon); groups.forEach(this::addGroup); } @Override public boolean isTutorial() { return tutorial; } @Override public void setTutorial(boolean tutorial) { this.tutorial = tutorial; } @Override public List getGroups() { return new ArrayList<>(groups); } @Override public void addGroup(PlayerGroup group) { groups.add(group); ((DGroup) group).setGame(this); group.setInitialLives(getRules().getState(GameRule.INITIAL_GROUP_LIVES)); group.setLives(getRules().getState(GameRule.INITIAL_GROUP_LIVES)); GameGoal goal = getRules().getState(GameRule.GAME_GOAL); if (goal.getType().hasComponent(GameGoal.INITIAL_SCORE)) { group.setScore(goal.getState(GameGoal.INITIAL_SCORE)); } } @Override public void removeGroup(PlayerGroup group) { groups.remove(group); if (groups.isEmpty()) { delete(); } } @Override public boolean hasStarted() { return started; } @Override public void setStarted(boolean started) { this.started = started; } @Override public Dungeon getDungeon() { return dungeon; } /** * Sets up all dungeon-related fields. * * @param dungeon the dungeon to set */ public void setDungeon(Dungeon dungeon) { this.dungeon = dungeon; if (dungeon.isMultiFloor()) { unplayedFloors = dungeon.getFloors(); } } /** * Sets up all dungeon-related fields. * * @param name the name of the dungeon * @return if the action was successful */ public boolean setDungeon(String name) { dungeon = plugin.getDungeonRegistry().get(name); if (dungeon != null) { unplayedFloors = dungeon.getFloors(); return true; } else { ResourceWorld resource = plugin.getMapRegistry().get(name); if (resource != null) { dungeon = resource.getSingleFloorDungeon(); return true; } return false; } } @Override public DGameWorld getWorld() { return (DGameWorld) world; } @Override public void setWorld(GameWorld gameWorld) { world = gameWorld; } @Override public List getUnplayedFloors() { return unplayedFloors; } @Override public boolean addUnplayedFloor(ResourceWorld unplayedFloor) { return unplayedFloors.add(unplayedFloor); } @Override public boolean removeUnplayedFloor(ResourceWorld unplayedFloor, boolean force) { if (getDungeon().getRemoveWhenPlayed() || force) { return unplayedFloors.remove(unplayedFloor); } return false; } @Override public ResourceWorld getNextFloor() { return nextFloor; } @Override public void setNextFloor(ResourceWorld floor) { nextFloor = floor; } @Override public int getFloorCount() { return floorCount; } @Override public boolean hasRewards() { return rewards; } @Override public void setRewards(boolean enabled) { rewards = enabled; } /** * @return the waveCount */ public int getWaveCount() { return waveCount; } /** * @param waveCount the waveCount to set */ public void setWaveCount(int waveCount) { this.waveCount = waveCount; } /** * @return how many mobs have been killed in the game */ public int getGameKills() { int count = 0; for (String killer : gameKills.keySet()) { count += gameKills.get(killer); } return count; } /** * @return how many mobs have been killed in the last game */ public int getWaveKills() { int count = 0; for (String killer : waveKills.keySet()) { count += waveKills.get(killer); } return count; } /** * @param killer the killer; null if the killer is not a player */ public void addKill(String killer) { if (killer == null) { killer = "N/A"; } waveKills.put(killer, waveKills.get(killer) == null ? 1 : waveKills.get(killer) + 1); } /** * Adds the values of the wave kills map to the game kills map and resets the wave kills. */ public void resetWaveKills() { gameKills.putAll(waveKills); waveKills.clear(); } @Override public Collection getPlayers() { Set toReturn = new HashSet<>(); for (PlayerGroup group : groups) { toReturn.addAll(group.getMembers().getOnlinePlayers()); } return toReturn; } @Override public boolean isEmpty() { return groups.isEmpty(); } @Override public GameWorld ensureWorldIsLoaded(boolean ignoreLimit) { if (world != null) { return world; } world = dungeon.getMap().instantiateGameWorld(this, ignoreLimit); return world; } @Override public boolean start() { getWorld().setWeather(getRules()); for (PlayerGroup group : groups) { if (group == null) { continue; } if (!((DGroup) group).checkStartGame(this)) { MessageUtil.debug(plugin, "Could not start game for group " + group); return false; } } int i = 0; for (PlayerGroup group : groups) { if (group != null) { ((DGroup) group).startGame(this, i++); } } if (getWorld() != null) { if (!getWorld().isPlaying()) { getWorld().startGame(); } } floorCount++; nextFloor = null; started = true; return true; } @Override public void delete() { GameSign gameSign = GameSign.getByGame(plugin, this); plugin.getGameCache().remove(this); if (gameSign != null) { gameSign.update(); } } /** * @param mobCountIncreaseRate the new mob count will be increased by this rate * @param teleport whether or not to teleport the players to the start location */ public void finishWave(final double mobCountIncreaseRate, final boolean teleport) { waveCount++; resetWaveKills(); for (Trigger uncasted : getWorld().getTriggers()) { if (!(uncasted instanceof ProgressTrigger)) { continue; } ProgressTrigger trigger = (ProgressTrigger) uncasted; if (getWaveCount() >= trigger.getWaveCount() & getFloorCount() >= trigger.getFloorCount() - 1 || !getUnplayedFloors().contains(trigger.getFloor()) & trigger.getFloor() != null) { trigger.trigger(true, null); } } int delay = getRules().getState(GameRule.TIME_TO_NEXT_WAVE); sendMessage(DMessage.GROUP_WAVE_FINISHED.getMessage(String.valueOf(waveCount), String.valueOf(delay))); new BukkitRunnable() { @Override public void run() { if (teleport) { groups.forEach(g -> g.getMembers().getOnlinePlayers().forEach(p -> p.teleport(world.getStartLocation(plugin.getPlayerGroup(p))))); } for (DungeonSign dSign : world.getDungeonSigns()) { if (!(dSign instanceof MobSign)) { continue; } MobSign mobSign = (MobSign) dSign; int newAmount = (int) Math.ceil(mobSign.getInitialAmount() * mobCountIncreaseRate); mobSign.setN(newAmount); mobSign.startTask(); } } }.runTaskLater(plugin, delay * 20); } @Override public boolean isFinished() { return groups.stream().allMatch(PlayerGroup::isFinished); } @Override public String toString() { return getClass().getSimpleName() + "{dungeon=" + getDungeon() + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/dungeon/DungeonConfig.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.dungeon; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.world.WorldConfig; import de.erethon.xlib.config.DREConfig; import java.io.File; import java.util.ArrayList; import java.util.List; /** * Represents a dungeon script. See {@link de.erethon.dungeonsxl.dungeon.DDungeon}. * * @author Daniel Saukel */ public class DungeonConfig extends DREConfig { private DungeonsXL plugin; public static final int CONFIG_VERSION = 1; private ResourceWorld startFloor; private ResourceWorld endFloor; private List floors = new ArrayList<>(); private int floorCount; private boolean removeWhenPlayed; private WorldConfig overrideValues; private WorldConfig defaultValues; public DungeonConfig(DungeonsXL plugin, File file) { super(file, CONFIG_VERSION); this.plugin = plugin; if (initialize) { initialize(); } load(); } public ResourceWorld getStartFloor() { return startFloor; } public void setStartFloor(ResourceWorld startFloor) { this.startFloor = startFloor; } public ResourceWorld getEndFloor() { return endFloor; } public void setEndFloor(ResourceWorld endFloor) { this.endFloor = endFloor; } public List getFloors() { return floors; } public void addFloor(ResourceWorld resource) { floors.add(resource); } public void removeFloor(ResourceWorld resource) { floors.remove(resource); } public int getFloorCount() { return floorCount; } public void setFloorCount(int floorCount) { this.floorCount = floorCount; } public boolean getRemoveWhenPlayed() { return removeWhenPlayed; } public void setRemoveWhenPlayed(boolean removeWhenPlayed) { this.removeWhenPlayed = removeWhenPlayed; } public WorldConfig getOverrideValues() { return overrideValues; } public WorldConfig getDefaultValues() { return defaultValues; } public boolean containsFloor(ResourceWorld resource) { return floors.contains(resource) || startFloor.equals(resource) || endFloor.equals(resource); } public boolean containsFloor(String mapName) { return containsFloor(plugin.getMapRegistry().get(mapName)); } @Override public void load() { for (String floor : config.getStringList("floors")) { ResourceWorld resource = plugin.getMapRegistry().get(floor); if (resource != null) { floors.add(resource); } } startFloor = plugin.getMapRegistry().get(config.getString("startFloor")); endFloor = plugin.getMapRegistry().get(config.getString("endFloor")); floorCount = config.getInt("floorCount", floors.size() + 2); removeWhenPlayed = config.getBoolean("removeWhenPlayed", removeWhenPlayed); overrideValues = new WorldConfig(plugin, config.getConfigurationSection("overrideValues")); defaultValues = new WorldConfig(plugin, config.getConfigurationSection("defaultValues")); } @Override public String toString() { return getClass().getSimpleName() + "{file=" + file.getPath() + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/DPortal.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.util.BlockUtil; import java.util.HashSet; import java.util.Set; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * A portal that leads into a dungeon. * * @author Frank Baumann, Daniel Saukel */ public class DPortal extends GlobalProtection { private Block block1; private Block block2; private ExItem material = VanillaItem.NETHER_PORTAL; private boolean zAxis; private boolean active; private Set blocks; public DPortal(DungeonsXL plugin, int id, World world, ExItem material, boolean active) { super(plugin, world, id); this.material = material; this.active = active; } public DPortal(DungeonsXL plugin, World world, int id, ConfigurationSection config) { super(plugin, world, id); block1 = world.getBlockAt(config.getInt("loc1.x"), config.getInt("loc1.y"), config.getInt("loc1.z")); block2 = world.getBlockAt(config.getInt("loc2.x"), config.getInt("loc2.y"), config.getInt("loc2.z")); material = plugin.getXLib().getExItem(config.getString("material")); if (material == null) { material = VanillaItem.NETHER_PORTAL; } String axis = config.getString("axis"); zAxis = axis != null && axis.equalsIgnoreCase("z"); active = true; create(null); } /** * @return the block1 */ public Block getBlock1() { return block1; } /** * @param block1 the block1 to set */ public void setBlock1(Block block1) { this.block1 = block1; } /** * @return the block2 */ public Block getBlock2() { return block2; } /** * @param block2 the block2 to set */ public void setBlock2(Block block2) { this.block2 = block2; } /** * @return if the portal is active */ public boolean isActive() { return active; } /** * @param active set the DPortal active */ public void setActive(boolean active) { this.active = active; } /** * Create a new DPortal * * @param player the creator */ public void create(DGlobalPlayer player) { if (block1 == null || block2 == null) { delete(); return; } if (player != null && material == VanillaItem.NETHER_PORTAL) { float yaw = player.getPlayer().getLocation().getYaw(); if (yaw >= 45 & yaw < 135 || yaw >= 225 & yaw < 315) { zAxis = true; } else if (yaw >= 315 | yaw < 45 || yaw >= 135 & yaw < 225) { zAxis = false; } } int x1 = block1.getX(), y1 = block1.getY(), z1 = block1.getZ(); int x2 = block2.getX(), y2 = block2.getY(), z2 = block2.getZ(); int xcount = 0, ycount = 0, zcount = 0; if (x1 > x2) { xcount = -1; } else if (x1 < x2) { xcount = 1; } if (y1 > y2) { ycount = -1; } else if (y1 < y2) { ycount = 1; } if (z1 > z2) { zcount = -1; } else if (z1 < z2) { zcount = 1; } int xx = x1; do { int yy = y1; do { int zz = z1; do { Material type = getWorld().getBlockAt(xx, yy, zz).getType(); if (!type.isSolid()) { Block block = getWorld().getBlockAt(xx, yy, zz); block.setType(material.getMaterial(), false); if (material == VanillaItem.NETHER_PORTAL) { DungeonsXL.BLOCK_ADAPTER.setAxis(block, zAxis); } } zz = zz + zcount; } while (zz != z2 + zcount); yy = yy + ycount; } while (yy != y2 + ycount); xx = xx + xcount; } while (xx != x2 + xcount); if (player == null) { return; } player.getPlayer().getInventory().setItemInHand(player.getCachedItem()); player.setCachedItem(null); if (material != VanillaItem.NETHER_PORTAL) { player.sendMessage(DMessage.PLAYER_PORTAL_CREATED.getMessage()); player.setCreatingPortal(null); } else { ClickEvent onClickYes = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/dungeonsxl portal " + VanillaItem.NETHER_PORTAL.getId() + " -rotate"); TextComponent yes = new TextComponent(DMessage.BUTTON_ACCEPT.getMessage()); yes.setClickEvent(onClickYes); ClickEvent onClickNo = new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/dungeonsxl portal " + VanillaItem.NETHER_PORTAL.getId() + " -norotate"); TextComponent no = new TextComponent(DMessage.BUTTON_DENY.getMessage()); no.setClickEvent(onClickNo); player.sendMessage(DMessage.PLAYER_PORTAL_ROTATE.getMessage()); player.getPlayer().spigot().sendMessage(yes, new TextComponent(" "), no); } } public void rotate() { zAxis = !zAxis; for (Block block : getBlocks()) { if (block.getType() == VanillaItem.NETHER_PORTAL.getMaterial()) { DungeonsXL.BLOCK_ADAPTER.setAxis(block, zAxis); } } } /** * @param player the player to teleport into his dungeon */ public void teleport(Player player) { if (plugin.isLoadingWorld()) { return; } PlayerGroup group = plugin.getPlayerGroup(player); if (group == null) { MessageUtil.sendActionBarMessage(player, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } Dungeon dungeon = group.getDungeon(); if (dungeon == null) { MessageUtil.sendActionBarMessage(player, DMessage.ERROR_NO_SUCH_DUNGEON.getMessage()); return; } if (!plugin.getPlayerCache().get(player).checkRequirements(dungeon)) { return; } Game game = group.getGame(); if (game == null) { game = new DGame(plugin, dungeon, group); } GameWorld target = game.ensureWorldIsLoaded(false); if (target == null) { MessageUtil.sendActionBarMessage(player, DMessage.ERROR_TOO_MANY_INSTANCES.getMessage()); return; } new DGamePlayer(plugin, player, target); } @Override public Set getBlocks() { if (blocks == null) { if (block1 != null && block2 != null) { blocks = BlockUtil.getBlocksBetween(block1, block2); } else { blocks = new HashSet<>(); } } return blocks; } @Override public String getDataPath() { return "protections.portals"; } @Override public void save(ConfigurationSection config) { if (!active) { return; } config.set("loc1.x", block1.getX()); config.set("loc1.y", block1.getY()); config.set("loc1.z", block1.getZ()); config.set("loc2.x", block2.getX()); config.set("loc2.y", block2.getY()); config.set("loc2.z", block2.getZ()); config.set("material", material.getId()); if (material == VanillaItem.NETHER_PORTAL) { config.set("axis", zAxis ? "z" : "x"); } } @Override public void delete() { plugin.getGlobalProtectionCache().removeProtection(this); if (block1 == null || block2 == null) { return; } int x1 = block1.getX(), y1 = block1.getY(), z1 = block1.getZ(); int x2 = block2.getX(), y2 = block2.getY(), z2 = block2.getZ(); int xcount = 0, ycount = 0, zcount = 0; if (x1 > x2) { xcount = -1; } else if (x1 < x2) { xcount = 1; } if (y1 > y2) { ycount = -1; } else if (y1 < y2) { ycount = 1; } if (z1 > z2) { zcount = -1; } else if (z1 < z2) { zcount = 1; } int xx = x1; do { int yy = y1; do { int zz = z1; do { Material type = getWorld().getBlockAt(xx, yy, zz).getType(); if (material.getMaterial() == type) { getWorld().getBlockAt(xx, yy, zz).setType(Material.AIR); } zz = zz + zcount; } while (zz != z2 + zcount); yy = yy + ycount; } while (yy != y2 + ycount); xx = xx + xcount; } while (xx != x2 + xcount); } /* Statics */ /** * @param plugin the plugin instance * @param location a location covered by the returned portal * @return the portal at the location, null if there is none */ public static DPortal getByLocation(DungeonsXL plugin, Location location) { return getByBlock(plugin, location.getBlock()); } /** * @param plugin the plugin instance * @param block a block covered by the returned portal * @return the portal that the block belongs to, null if it belongs to none */ public static DPortal getByBlock(DungeonsXL plugin, Block block) { if (plugin.isInstance(block.getWorld())) { return null; } for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections(DPortal.class)) { DPortal portal = (DPortal) protection; if (!portal.getWorld().equals(block.getWorld()) || portal.getBlock1() == null || portal.getBlock2() == null) { continue; } int x1 = portal.block1.getX(), y1 = portal.block1.getY(), z1 = portal.block1.getZ(); int x2 = portal.block2.getX(), y2 = portal.block2.getY(), z2 = portal.block2.getZ(); int x3 = block.getX(), y3 = block.getY(), z3 = block.getZ(); if (x1 > x2) { if (x3 < x2 || x3 > x1) { continue; } } else if (x3 > x2 || x3 < x1) { continue; } if (y1 > y2) { if (y3 < y2 || y3 > y1) { continue; } } else if (y3 > y2 || y3 < y1) { continue; } if (z1 > z2) { if (z3 < z2 || z3 > z1) { continue; } } else if (z3 > z2 || z3 < z1) { continue; } return portal; } return null; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/GameSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.xlib.category.Category; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; /** * Basically a GroupSign, but to form a game of multiple groups. * * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class GameSign extends JoinSign { public static final String GAME_SIGN_TAG = "Game"; private DGame game; public GameSign(DungeonsXL plugin, int id, Block startSign, String identifier, int maxGroupsPerGame, int startIfElementsAtLeast) { super(plugin, id, startSign, identifier, maxGroupsPerGame, startIfElementsAtLeast); } public GameSign(DungeonsXL plugin, World world, int id, ConfigurationSection config) { super(plugin, world, id, config); } /** * @return the attached game */ public DGame getGame() { return game; } /** * @param game the game to set */ public void setGame(DGame game) { this.game = game; } /** * Update this game sign to show the game(s) correctly. */ @Override public void update() { if (!(startSign.getState() instanceof Sign)) { return; } super.update(); Sign sign = (Sign) startSign.getState(); if (game == null || game.getGroups().isEmpty()) { loadedWorld = false; sign.setLine(0, DMessage.SIGN_GLOBAL_NEW_GAME.getMessage()); sign.update(); return; } if (game.getGroups().size() >= startIfElementsAtLeast && startIfElementsAtLeast != -1) { loadedWorld = true; game.getGroups().forEach(g -> ((DGroup) g).teleport()); } if (game.hasStarted()) { sign.setLine(0, DMessage.SIGN_GLOBAL_IS_PLAYING.getMessage()); } else if (game.getGroups().size() >= maxElements) { sign.setLine(0, DMessage.SIGN_GLOBAL_FULL.getMessage()); } else { sign.setLine(0, DMessage.SIGN_GLOBAL_JOIN_GAME.getMessage()); } int j = 1; Sign rowSign = sign; for (PlayerGroup dGroup : game.getGroups()) { if (j > 3) { j = 0; rowSign = (Sign) sign.getBlock().getRelative(0, -1, 0).getState(); } if (rowSign != null) { rowSign.setLine(j, dGroup.getName()); } j++; rowSign.update(); } sign.update(); } @Override public String getDataPath() { return "protections.gameSigns"; } public void onPlayerInteract(Block block, Player player) { DGroup dGroup = (DGroup) plugin.getPlayerGroup(player); if (dGroup == null) { MessageUtil.sendMessage(player, DMessage.ERROR_JOIN_GROUP.getMessage()); return; } if (!dGroup.getLeader().equals(player)) { MessageUtil.sendMessage(player, DMessage.ERROR_NOT_LEADER.getMessage()); return; } if (dGroup.getGame() != null) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_GAME.getMessage()); return; } Block topBlock = block.getRelative(0, startSign.getY() - block.getY(), 0); if (!(topBlock.getState() instanceof Sign)) { return; } Sign topSign = (Sign) topBlock.getState(); if (topSign.getLine(0).equals(DMessage.SIGN_GLOBAL_NEW_GAME.getMessage())) { if (dungeon == null) { MessageUtil.sendMessage(player, DMessage.ERROR_SIGN_WRONG_FORMAT.getMessage()); return; } dGroup.setDungeon(dungeon); game = new DGame(plugin, dungeon, dGroup); update(); } else if (topSign.getLine(0).equals(DMessage.SIGN_GLOBAL_JOIN_GAME.getMessage())) { dGroup.setDungeon(dungeon); game.addGroup(dGroup); update(); } } /* Statics */ /** * @param plugin the plugin instance * @param block a block which is protected by the returned GameSign * @return the game sign the block belongs to, null if it belongs to none */ public static GameSign getByBlock(DungeonsXL plugin, Block block) { if (!Category.SIGNS.containsBlock(block)) { return null; } for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections(GameSign.class)) { GameSign gameSign = (GameSign) protection; Block start = gameSign.startSign; if (start == block || (start.getX() == block.getX() && start.getZ() == block.getZ() && (start.getY() >= block.getY() && start.getY() - gameSign.verticalSigns <= block.getY()))) { return gameSign; } } return null; } /** * @param plugin the plugin instance * @param game the game to check * @return the game that this sign creates */ public static GameSign getByGame(DungeonsXL plugin, DGame game) { for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections(GameSign.class)) { GameSign gameSign = (GameSign) protection; if (gameSign.game == game) { return gameSign; } } return null; } public static GameSign tryToCreate(DungeonsXL plugin, SignChangeEvent event) { if (!event.getLine(0).equalsIgnoreCase(SIGN_TAG)) { return null; } if (!event.getLine(1).equalsIgnoreCase(GAME_SIGN_TAG)) { return null; } String identifier = event.getLine(2); String[] data = event.getLine(3).split(","); int maxGroupsPerGame = NumberUtil.parseInt(data[0], 1); int startIfElementsAtLeast = -1; if (data.length > 1) { startIfElementsAtLeast = NumberUtil.parseInt(data[1], -1); } return tryToCreate(plugin, event.getBlock(), identifier, maxGroupsPerGame, startIfElementsAtLeast); } public static GameSign tryToCreate(DungeonsXL plugin, Block startSign, String identifier, int maxElements, int startIfElementsAtLeast) { onCreation(plugin, startSign, identifier, maxElements, startIfElementsAtLeast); GameSign sign = new GameSign(plugin, plugin.getGlobalProtectionCache().generateId(GameSign.class, startSign.getWorld()), startSign, identifier, maxElements, startIfElementsAtLeast); plugin.getGlobalProtectionCache().addProtection(sign); return sign; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/GlobalProtection.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.xlib.chat.MessageUtil; import java.util.Collection; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; /** * @author Daniel Saukel */ public abstract class GlobalProtection { protected DungeonsXL plugin; public static final String SIGN_TAG = "[DXL]"; private String world; private int id; protected GlobalProtection(DungeonsXL plugin, World world, int id) { this.plugin = plugin; this.world = world.getName(); this.id = id; } /** * @return the world */ public World getWorld() { return Bukkit.getWorld(world); } /** * @return the id */ public int getId() { return id; } /* Actions */ /** * Delete this protection. */ public void delete() { plugin.getGlobalProtectionCache().removeProtection(this); } public boolean onBreak(DGlobalPlayer dPlayer) { if (dPlayer.isInBreakMode()) { delete(); MessageUtil.sendMessage(dPlayer.getPlayer(), DMessage.PLAYER_PROTECTED_BLOCK_DELETED.getMessage()); MessageUtil.sendMessage(dPlayer.getPlayer(), DMessage.CMD_BREAK_PROTECTED_MODE.getMessage()); dPlayer.setInBreakMode(false); return false; } else { return true; } } /* Abstracts */ /** * @return the path in the global data file */ public abstract String getDataPath(); public abstract void save(ConfigurationSection config); public UnloadedProtection unload() { String path = getDataPath() + "." + getWorld().getName() + "." + getId(); ConfigurationSection config; if (!plugin.getGlobalProtectionCache().getConfig().contains(path)) { config = plugin.getGlobalProtectionCache().getConfig().createSection(path); } else { config = plugin.getGlobalProtectionCache().getConfig().getConfigurationSection(path); } return UnloadedProtection.create(plugin, getClass(), getWorld().getName(), getId(), config); } /** * @return a collection of all blocks covered by this protection */ public abstract Collection getBlocks(); @Override public String toString() { return getClass().getSimpleName() + "{ID=" + id + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/GlobalProtectionCache.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.xlib.util.NumberUtil; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; /** * @author Daniel Saukel */ public class GlobalProtectionCache { private DungeonsXL plugin; private File file; private FileConfiguration config; private Set protections = new HashSet<>(); private Map unloaded = new HashMap<>(); public GlobalProtectionCache(DungeonsXL plugin) { this.plugin = plugin; file = new File(plugin.getDataFolder(), "data.yml"); if (!file.exists()) { try { file.createNewFile(); } catch (IOException exception) { exception.printStackTrace(); } } config = YamlConfiguration.loadConfiguration(file); } public FileConfiguration getConfig() { return config; } /** * @param location the location to check * @return the protection which covers this location */ public GlobalProtection getByLocation(Location location) { return getByBlock(location.getBlock()); } /** * @param block the block to check * @return the protection which covers this block */ public GlobalProtection getByBlock(Block block) { for (GlobalProtection protection : protections) { if (protection.getBlocks().contains(block)) { return protection; } } return null; } /** * @return the protections */ public Set getProtections() { return protections; } /** * @return the protections that are known but not loaded yet */ public Map getUnloadedProtections() { return unloaded; } /** * @param type All protections which are an instance of it will be returned. * @return the protections of the type */ public Set getProtections(Class type) { Set protectionsOfType = new HashSet<>(); for (GlobalProtection protection : protections) { if (protection.getClass() == type) { protectionsOfType.add(protection); } } return protectionsOfType; } /** * @param protection the protection type to add */ public void addProtection(GlobalProtection protection) { protections.add(protection); } /** * @param protection the protection to remove */ public void removeProtection(GlobalProtection protection) { protections.remove(protection); } public void loadAll() { ConfigurationSection gameSigns = config.getConfigurationSection("protections.gameSigns"); ConfigurationSection groupSigns = config.getConfigurationSection("protections.groupSigns"); ConfigurationSection leaveSigns = config.getConfigurationSection("protections.leaveSigns"); ConfigurationSection portals = config.getConfigurationSection("protections.portals"); if (gameSigns != null) { for (String worldName : gameSigns.getValues(false).keySet()) { ConfigurationSection ws = gameSigns.getConfigurationSection(worldName); if (ws == null) { continue; } for (Entry entry : ws.getValues(false).entrySet()) { World world = Bukkit.getWorld(worldName); if (world != null) { addProtection(new GameSign(plugin, world, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey()))); } else { UnloadedProtection protection = UnloadedProtection.create(plugin, GameSign.class, worldName, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey())); unloaded.put(protection, worldName); } } } } if (groupSigns != null) { for (String worldName : groupSigns.getValues(false).keySet()) { ConfigurationSection ws = groupSigns.getConfigurationSection(worldName); if (ws == null) { continue; } for (Entry entry : ws.getValues(false).entrySet()) { World world = Bukkit.getWorld(worldName); if (world != null) { addProtection(new GroupSign(plugin, world, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey()))); } else { UnloadedProtection protection = UnloadedProtection.create(plugin, GroupSign.class, worldName, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey())); unloaded.put(protection, worldName); } } } } if (leaveSigns != null) { for (String worldName : leaveSigns.getValues(false).keySet()) { ConfigurationSection ws = leaveSigns.getConfigurationSection(worldName); if (ws == null) { continue; } for (Entry entry : ws.getValues(false).entrySet()) { World world = Bukkit.getWorld(worldName); if (world != null) { addProtection(new LeaveSign(plugin, world, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey()))); } else { UnloadedProtection protection = UnloadedProtection.create(plugin, LeaveSign.class, worldName, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey())); unloaded.put(protection, worldName); } } } } if (portals != null) { for (String worldName : portals.getValues(false).keySet()) { ConfigurationSection ws = portals.getConfigurationSection(worldName); if (ws == null) { continue; } for (Entry entry : ws.getValues(false).entrySet()) { World world = Bukkit.getWorld(worldName); if (world != null) { addProtection(new DPortal(plugin, world, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey()))); } else { UnloadedProtection protection = UnloadedProtection.create(plugin, DPortal.class, worldName, NumberUtil.parseInt(entry.getKey()), ws.getConfigurationSection(entry.getKey())); unloaded.put(protection, worldName); } } } } } public void saveAll() { if (!file.exists()) { try { file.createNewFile(); } catch (IOException exception) { exception.printStackTrace(); } } for (GlobalProtection protection : protections) { String path = protection.getDataPath() + "." + protection.getWorld().getName() + "." + protection.getId(); if (!config.contains(path)) { protection.save(config.createSection(path)); } } try { config.save(file); } catch (IOException exception) { exception.printStackTrace(); } } /** * @param type Each type is stored seperately. * @param world Each world has its own IDs. * @return an unused ID number for a new protection */ public int generateId(Class type, World world) { int id = 1; for (GlobalProtection protection : protections) { if (protection.getClass() == type && id <= protection.getId()) { id = protection.getId() + 1; } } return id; } /** * @param block the block to check * @return if the block is protected by a GlobalProtection */ public boolean isProtectedBlock(Block block) { for (GlobalProtection protection : protections) { if (protection.getBlocks().contains(block)) { return true; } } return false; } public void updateGroupSigns(DGroup dGroupSearch) { for (GlobalProtection protection : getProtections(GroupSign.class)) { GroupSign groupSign = (GroupSign) protection; if (dGroupSearch != null && groupSign.getGroup() == dGroupSearch) { if (dGroupSearch.isEmpty()) { groupSign.setGroup(null); } groupSign.update(); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/GlobalProtectionListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.player.DPlayerListener; import de.erethon.xlib.category.Category; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import java.util.Collection; import java.util.List; import java.util.Map.Entry; import java.util.Set; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.player.PlayerBucketFillEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerPortalEvent; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.event.world.WorldUnloadEvent; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel, Wooyoung Son, Frank Baumann, Milan Albrecht */ public class GlobalProtectionListener implements Listener { private DungeonsXL plugin; public GlobalProtectionListener(DungeonsXL plugin) { this.plugin = plugin; } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockBreakWithSignOnIt(BlockBreakEvent event) { Block block = event.getBlock(); Player player = event.getPlayer(); Block blockAbove = block.getRelative(BlockFace.UP); //get the above block and return if there is nothing if (blockAbove == null) { return; } //return if above block is not a sign if (!Category.SIGNS.containsBlock(blockAbove)) { return; } //let onBreak() method to handle the sign BlockBreakEvent bbe = new BlockBreakEvent(blockAbove, player); onBlockBreak(bbe); //follow the onBreak() event.setCancelled(bbe.isCancelled()); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockBreak(BlockBreakEvent event) { Block block = event.getBlock(); Player player = event.getPlayer(); DGlobalPlayer dGlobalPlayer = (DGlobalPlayer) plugin.getPlayerCache().get(player); GlobalProtection protection = plugin.getGlobalProtectionCache().getByBlock(block); if (protection != null) { if (protection.onBreak(dGlobalPlayer)) { event.setCancelled(true); } } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockPlace(BlockPlaceEvent event) { if (DPortal.getByBlock(plugin, event.getBlock()) != null) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onPlayerBucketFill(PlayerBucketFillEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player)) { return; } Block block = event.getBlockClicked(); if (DPortal.getByBlock(plugin, block) != null) { event.setCancelled(true); // Workaround for a bug of Bukkit event.getPlayer().sendBlockChange(block.getLocation(), block.getType(), (byte) 0); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockSpread(BlockSpreadEvent event) { if (DPortal.getByBlock(plugin, event.getBlock()) != null) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockPhysics(BlockPhysicsEvent event) { if (DPortal.getByBlock(plugin, event.getBlock()) != null) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onEntityExplode(EntityExplodeEvent event) { List blocklist = event.blockList(); for (Block block : blocklist) { if (plugin.getGlobalProtectionCache().isProtectedBlock(block)) { event.setCancelled(true); } } } @EventHandler public void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player) || plugin.isInstance(event.getTo().getWorld())) { return; } if (!plugin.getMainConfig().isStrictMovementCheckEnabled()) { Block blockFrom = event.getFrom().getBlock(); Block blockTo = event.getTo().getBlock(); if (blockFrom.equals(blockTo)) { return; } } DPortal dPortal = DPortal.getByLocation(plugin, player.getEyeLocation()); if (dPortal == null) { return; } dPortal.teleport(player); } @EventHandler public void onPlayerPortal(PlayerPortalEvent event) { Block block1 = event.getFrom().getBlock(); Block block2 = block1.getRelative(BlockFace.UP); Block block3 = block2.getRelative(BlockFace.UP); Block block4 = block1.getRelative(BlockFace.DOWN); if (isPortalInNearBy(block1) || isPortalInNearBy(block2) || isPortalInNearBy(block3) || isPortalInNearBy(block4)) { event.setCancelled(true); } } private boolean isPortalInNearBy(Block block1) { Block block2 = block1.getRelative(BlockFace.WEST); Block block3 = block1.getRelative(BlockFace.NORTH); Block block4 = block1.getRelative(BlockFace.EAST); Block block5 = block1.getRelative(BlockFace.SOUTH); Block block6 = block2.getRelative(BlockFace.NORTH); Block block7 = block2.getRelative(BlockFace.SOUTH); Block block8 = block4.getRelative(BlockFace.NORTH); Block block9 = block4.getRelative(BlockFace.SOUTH); return (DPortal.getByBlock(plugin, block1) != null || DPortal.getByBlock(plugin, block2) != null || DPortal.getByBlock(plugin, block3) != null || DPortal.getByBlock(plugin, block4) != null || DPortal.getByBlock(plugin, block5) != null || DPortal.getByBlock(plugin, block6) != null || DPortal.getByBlock(plugin, block7) != null || DPortal.getByBlock(plugin, block8) != null || DPortal.getByBlock(plugin, block9) != null); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onPortalCreation(PlayerInteractEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player)) { return; } DGlobalPlayer dPlayer = (DGlobalPlayer) plugin.getPlayerCache().get(player); if (!dPlayer.isCreatingPortal()) { return; } ItemStack item = event.getItem(); Block block = event.getClickedBlock(); if (item == null || !VanillaItem.WOODEN_SWORD.is(item) || block == null) { return; } for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections(DPortal.class)) { DPortal dPortal = (DPortal) protection; if (dPortal.isActive() || dPortal != dPlayer.getPortal()) { continue; } if (dPortal.getBlock1() == null) { dPortal.setBlock1(event.getClickedBlock()); dPlayer.sendMessage(DMessage.PLAYER_PORTAL_PROGRESS.getMessage()); } else if (dPortal.getBlock2() == null) { dPortal.setBlock2(event.getClickedBlock()); dPortal.setActive(true); dPortal.create(dPlayer); } event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player) || plugin.getPlayerCache().get(player).isInBreakMode()) { return; } Block clickedBlock = event.getClickedBlock(); if (clickedBlock == null) { return; } if (Category.SIGNS.containsBlock(clickedBlock)) { GroupSign groupSign = GroupSign.getByBlock(plugin, clickedBlock); if (groupSign != null) { groupSign.onPlayerInteract(clickedBlock, player); event.setCancelled(true); } GameSign gameSign = GameSign.getByBlock(plugin, clickedBlock); if (gameSign != null) { gameSign.onPlayerInteract(clickedBlock, player); event.setCancelled(true); } LeaveSign leaveSign = LeaveSign.getByBlock(plugin, clickedBlock); if (leaveSign != null) { leaveSign.onPlayerInteract(player); event.setCancelled(true); } } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onSignChange(SignChangeEvent event) { Player player = event.getPlayer(); Block block = event.getBlock(); BlockState state = block.getState(); if (!(state instanceof Sign)) { return; } String[] lines = event.getLines(); // Group Signs if (plugin.getEditWorld(player.getWorld()) == null) { if (!DPermission.hasPermission(player, DPermission.SIGN)) { return; } if (!lines[0].equalsIgnoreCase(GlobalProtection.SIGN_TAG)) { return; } if (lines[1].equalsIgnoreCase(GroupSign.GROUP_SIGN_TAG)) { if (GroupSign.tryToCreate(plugin, event) != null) { event.setCancelled(true); } } else if (lines[1].equalsIgnoreCase(GameSign.GAME_SIGN_TAG)) { if (GameSign.tryToCreate(plugin, event) != null) { event.setCancelled(true); } } else if (lines[1].equalsIgnoreCase(LeaveSign.LEAVE_SIGN_TAG)) { Sign sign = (Sign) state; plugin.getGlobalProtectionCache().addProtection(new LeaveSign(plugin, plugin.getGlobalProtectionCache().generateId(LeaveSign.class, sign.getWorld()), sign)); event.setCancelled(true); } } } @EventHandler public void onWorldLoad(WorldLoadEvent event) { World world = event.getWorld(); MessageUtil.debug(plugin, "New world detected."); Set> protections = plugin.getGlobalProtectionCache().getUnloadedProtections().entrySet(); for (Entry entry : protections.toArray(new Entry[protections.size()])) { MessageUtil.debug(plugin, "Checking unloaded protection " + entry); if (world.getName().equals(entry.getValue())) { MessageUtil.debug(plugin, "New world has global DXL data: " + entry); plugin.getGlobalProtectionCache().addProtection(entry.getKey().load(world)); } } } @EventHandler public void onWorldUnload(WorldUnloadEvent event) { World world = event.getWorld(); if (world.getName().startsWith("DXL_")) { return; } MessageUtil.debug(plugin, "Unloaded world detected."); Collection protections = plugin.getGlobalProtectionCache().getProtections(); for (GlobalProtection protection : protections.toArray(new GlobalProtection[protections.size()])) { if (protection.getWorld().getName().equals(world.getName())) { protection.delete(); plugin.getGlobalProtectionCache().getUnloadedProtections().put(protection.unload(), world.getName()); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/GroupSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent.Cause; import de.erethon.dungeonsxl.api.player.GroupAdapter; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.xlib.category.Category; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; /** * A sign to form a group and to define its dungeon. * * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class GroupSign extends JoinSign { public static final String GROUP_SIGN_TAG = "Group"; private String groupName; private DGroup group; public GroupSign(DungeonsXL plugin, int id, Block startSign, String identifier, int maxPlayersPerGroup, int startIfElementsAtLeast, String groupName) { super(plugin, id, startSign, identifier, maxPlayersPerGroup, startIfElementsAtLeast); this.groupName = groupName; } public GroupSign(DungeonsXL plugin, World world, int id, ConfigurationSection config) { super(plugin, world, id, config); groupName = config.getString("groupName"); } /** * @return the attached group */ public DGroup getGroup() { return group; } /** * @param group the group to set */ public void setGroup(DGroup group) { this.group = group; } /** * Update this group sign to show the group(s) correctly. */ @Override public void update() { if (!(startSign.getState() instanceof Sign)) { return; } super.update(); Sign sign = (Sign) startSign.getState(); if (group == null) { loadedWorld = false; sign.setLine(0, DMessage.SIGN_GLOBAL_NEW_GROUP.getMessage()); sign.update(); return; } if (group.getMembers().size() >= startIfElementsAtLeast && startIfElementsAtLeast != -1 && !loadedWorld) { loadedWorld = true; group.teleport(); } if (group.isPlaying()) { sign.setLine(0, DMessage.SIGN_GLOBAL_IS_PLAYING.getMessage()); } else if (group.getMembers().size() >= maxElements) { sign.setLine(0, DMessage.SIGN_GLOBAL_FULL.getMessage()); } else { sign.setLine(0, DMessage.SIGN_GLOBAL_JOIN_GROUP.getMessage()); } int j = 1; Sign rowSign = sign; for (Player player : group.getMembers().getOnlinePlayers()) { if (j > 3) { j = 0; rowSign = (Sign) sign.getBlock().getRelative(0, -1, 0).getState(); } if (rowSign != null) { rowSign.setLine(j, player.getName()); } j++; rowSign.update(); } sign.update(); } @Override public String getDataPath() { return "protections.groupSigns"; } @Override public void save(ConfigurationSection config) { super.save(config); config.set("groupName", groupName); } public void onPlayerInteract(Block block, Player player) { DGroup playerGroup = (DGroup) plugin.getPlayerGroup(player); if (playerGroup != null) { if (playerGroup.getLeader().equals(player)) { if (playerGroup.getGroupSign() != null || group != null || maxElements < playerGroup.getMembers().size()) { MessageUtil.sendMessage(player, DMessage.ERROR_LEAVE_GROUP.getMessage()); return; } else { group = playerGroup; group.setGroupSign(this); group.setDungeon(dungeon); update(); return; } } else { playerGroup.removeMember(player, true); } } Block topBlock = block.getRelative(0, startSign.getY() - block.getY(), 0); if (!(topBlock.getState() instanceof Sign)) { return; } Sign topSign = (Sign) topBlock.getState(); if (topSign.getLine(0).equals(DMessage.SIGN_GLOBAL_NEW_GROUP.getMessage())) { if (dungeon == null) { MessageUtil.sendMessage(player, DMessage.ERROR_SIGN_WRONG_FORMAT.getMessage()); return; } for (GroupAdapter adapter : plugin.getGroupAdapters()) { if (adapter.isExternalGroupMember(player)) { group = (DGroup) adapter.getOrCreateDungeonGroup(adapter.getExternalGroup(player), maxElements); } } if (group == null) { group = DGroup.create(plugin, Cause.GROUP_SIGN, player, groupName, null, dungeon); } if (group != null) { group.setDungeon(dungeon); group.setGroupSign(this); update(); } } else if (topSign.getLine(0).equals(DMessage.SIGN_GLOBAL_JOIN_GROUP.getMessage())) { group.addMember(player); update(); } } /* Statics */ /** * @param plugin the plugin instance * @param block a block which is protected by the returned GroupSign * @return the group sign the block belongs to, null if it belongs to none */ public static GroupSign getByBlock(DungeonsXL plugin, Block block) { if (!Category.SIGNS.containsBlock(block)) { return null; } for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections(GroupSign.class)) { GroupSign groupSign = (GroupSign) protection; Block start = groupSign.startSign; if (start == block || (start.getX() == block.getX() && start.getZ() == block.getZ() && (start.getY() >= block.getY() && start.getY() - groupSign.verticalSigns <= block.getY()))) { return groupSign; } } return null; } public static GroupSign tryToCreate(DungeonsXL plugin, SignChangeEvent event) { if (!event.getLine(0).equalsIgnoreCase(SIGN_TAG)) { return null; } if (!event.getLine(1).equalsIgnoreCase(GROUP_SIGN_TAG)) { return null; } String identifier = event.getLine(2); String[] data = event.getLine(3).split(","); int maxPlayersPerGroup = 1; String groupName = null; int startIfElementsAtLeast = -1; if (data.length >= 1) { maxPlayersPerGroup = NumberUtil.parseInt(data[0], 1); } if (data.length >= 2) { groupName = data[1]; } if (data.length == 3) { startIfElementsAtLeast = NumberUtil.parseInt(data[2], -1); } return tryToCreate(plugin, event.getBlock(), identifier, maxPlayersPerGroup, startIfElementsAtLeast, groupName); } public static GroupSign tryToCreate(DungeonsXL plugin, Block startSign, String identifier, int maxElements, int startIfElementsAtLeast, String groupName) { onCreation(plugin, startSign, identifier, maxElements, startIfElementsAtLeast); GroupSign sign = new GroupSign(plugin, plugin.getGlobalProtectionCache().generateId(GroupSign.class, startSign.getWorld()), startSign, identifier, maxElements, startIfElementsAtLeast, groupName); plugin.getGlobalProtectionCache().addProtection(sign); return sign; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/JoinSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.util.LWCUtil; import java.util.HashSet; import java.util.Set; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public abstract class JoinSign extends GlobalProtection { protected Dungeon dungeon; protected int maxElements; protected int startIfElementsAtLeast = -1; protected Block startSign; protected int verticalSigns; protected Set blocks; protected boolean loadedWorld; protected JoinSign(DungeonsXL plugin, int id, Block startSign, String identifier, int maxElements, int startIfElementsAtLeast) { super(plugin, startSign.getWorld(), id); this.startSign = startSign; dungeon = plugin.getDungeonRegistry().get(identifier); verticalSigns = (int) Math.ceil((float) (1 + maxElements) / 4); this.maxElements = maxElements; if (startIfElementsAtLeast > 0 && startIfElementsAtLeast <= maxElements) { this.startIfElementsAtLeast = startIfElementsAtLeast; } // 1.20 doesn't like setting sign text in the same tick as the creation event new BukkitRunnable() { @Override public void run() { update(); } }.runTaskLater(plugin, 1L); } protected JoinSign(DungeonsXL plugin, World world, int id, ConfigurationSection config) { super(plugin, world, id); startSign = world.getBlockAt(config.getInt("x"), config.getInt("y"), config.getInt("z")); String identifier = config.getString("dungeon"); dungeon = plugin.getDungeonRegistry().get(identifier); // LEGACY if (config.contains("maxElements")) { maxElements = config.getInt("maxElements"); } else if (config.contains("maxGroupsPerGame")) { maxElements = config.getInt("maxGroupsPerGame"); } else if (config.contains("maxPlayersPerGroup")) { maxElements = config.getInt("maxPlayersPerGroup"); } startIfElementsAtLeast = config.getInt("startIfElementsAtLeast", -1); verticalSigns = (int) Math.ceil((float) (1 + maxElements) / 4); update(); } /** * @return the dungeon */ public Dungeon getDungeon() { return dungeon; } /** * @param dungeon the dungeon to set */ public void setDungeon(Dungeon dungeon) { this.dungeon = dungeon; } /** * @return the maximum element count per sign */ public int getMaxElements() { return maxElements; } /** * @param amount the maximum element count per sign */ public void setMaxElements(int amount) { maxElements = amount; } /** * Returns the minimum amount of elements required to start the dungeon countdown * * @return the minimum amount of elements required to start the dungeon countdown;
* -1 if the dungeon is not instantiated only through the sign. */ public int getStartIfElementsAtLeastAmount() { return startIfElementsAtLeast; } /** * Sets the minimum amount of elements required to start the dungeon countdown * * @param amount the amount to set */ public void setStartIfElementsAtLeastAmount(int amount) { if ((amount > 0 || amount == -1) && amount <= maxElements) { startIfElementsAtLeast = amount; } else { throw new IllegalArgumentException("startIfElementsAtLeastAmount is < 0 or < maxElements"); } } @Override public Set getBlocks() { if (blocks == null) { blocks = new HashSet<>(); HashSet toAdd = new HashSet<>(); int y = -1 * verticalSigns; while (y != 1) { blocks.add(startSign.getRelative(0, y, 0)); y++; } for (Block block : blocks) { int i = verticalSigns; do { i--; Block beneath = block.getLocation().add(0, -1 * i, 0).getBlock(); toAdd.add(beneath); toAdd.add(BlockUtilCompat.getAttachedBlock(beneath)); } while (i >= 0); } blocks.addAll(toAdd); } return blocks; } /** * Clears signs */ public void update() { int y = -1 * verticalSigns; while (startSign.getRelative(0, y + 1, 0).getState() instanceof Sign && y != 0) { Sign subsign = (Sign) startSign.getRelative(0, y + 1, 0).getState(); subsign.setLine(0, ""); subsign.setLine(1, ""); subsign.setLine(2, ""); subsign.setLine(3, ""); subsign.update(); y++; } } @Override public void save(ConfigurationSection config) { config.set("x", startSign.getX()); config.set("y", startSign.getY()); config.set("z", startSign.getZ()); if (dungeon != null) { config.set("dungeon", dungeon.getName()); } config.set("maxElements", maxElements); if (startIfElementsAtLeast != -1) { config.set("startIfElementsAtLeast", startIfElementsAtLeast); } } protected static void onCreation(DungeonsXL plugin, Block startSign, String identifier, int maxElements, int startIfElementsAtLeast) { World world = startSign.getWorld(); BlockFace facing = DungeonsXL.BLOCK_ADAPTER.getFacing(startSign); int x = startSign.getX(), y = startSign.getY(), z = startSign.getZ(); int verticalSigns = (int) Math.ceil((float) (1 + maxElements) / 4); while (verticalSigns > 1) { Block block = world.getBlockAt(x, y - verticalSigns + 1, z); block.setType(startSign.getType(), false); DungeonsXL.BLOCK_ADAPTER.setFacing(block, facing); verticalSigns--; } LWCUtil.removeProtection(startSign); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/LeaveSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.util.LWCUtil; import de.erethon.xlib.chat.MessageUtil; import java.util.HashSet; import java.util.Set; import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * A sign to leave a group. * * @author Frank Baumann */ public class LeaveSign extends GlobalProtection { public static final String LEAVE_SIGN_TAG = "Leave"; private Block sign; private Set blocks; public LeaveSign(DungeonsXL plugin, int id, Sign sign) { super(plugin, sign.getWorld(), id); this.sign = sign.getBlock(); // 1.20 doesn't like setting sign text in the same tick as the creation event new BukkitRunnable() { @Override public void run() { setText(); } }.runTaskLater(plugin, 1L); LWCUtil.removeProtection(sign.getBlock()); } public LeaveSign(DungeonsXL plugin, World world, int id, ConfigurationSection config) { super(plugin, world, id); sign = world.getBlockAt(config.getInt("x"), config.getInt("y"), config.getInt("z")); setText(); LWCUtil.removeProtection(sign); } /* Getters and setters */ @Override public Set getBlocks() { if (blocks == null) { blocks = new HashSet<>(); blocks.add(sign); blocks.add(BlockUtilCompat.getAttachedBlock(sign)); } return blocks; } /* Actions */ public void setText() { BlockState state = sign.getState(); if (state instanceof Sign) { Sign sign = (Sign) state; sign.setLine(0, ChatColor.BLUE + "############"); sign.setLine(1, DMessage.SIGN_LEAVE.getMessage()); sign.setLine(2, ""); sign.setLine(3, ChatColor.BLUE + "############"); sign.update(); } } public void onPlayerInteract(Player player) { GamePlayer gamePlayer = plugin.getPlayerCache().getGamePlayer(player); if (gamePlayer != null) { gamePlayer.leave(); } else { PlayerGroup group = plugin.getPlayerGroup(player); if (group != null) { group.removeMember(player); MessageUtil.sendMessage(player, DMessage.PLAYER_LEAVE_GROUP.getMessage()); } } } @Override public String getDataPath() { return "protections.leaveSigns"; } @Override public void save(ConfigurationSection config) { config.set("x", sign.getX()); config.set("y", sign.getY()); config.set("z", sign.getZ()); } /* Statics */ /** * @param plugin the plugin instance * @param block a block which is protected by the returned LeaveSign * @return the leave sign the block belongs to, null if it belongs to none */ public static LeaveSign getByBlock(DungeonsXL plugin, Block block) { for (GlobalProtection protection : plugin.getGlobalProtectionCache().getProtections(LeaveSign.class)) { LeaveSign leaveSign = (LeaveSign) protection; if (leaveSign.getBlocks().contains(block)) { return leaveSign; } } return null; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/global/UnloadedProtection.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.global; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.xlib.chat.MessageUtil; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import org.bukkit.World; import org.bukkit.configuration.ConfigurationSection; /** * @author Daniel Saukel */ public class UnloadedProtection { private DungeonsXL plugin; private GlobalProtectionCache cache; private Constructor constructor; private String worldName; private int id; private ConfigurationSection config; private UnloadedProtection() {} public static UnloadedProtection create(DungeonsXL plugin, Class type, String worldName, int id, ConfigurationSection config) { UnloadedProtection protection = new UnloadedProtection(); protection.plugin = plugin; try { protection.constructor = type.getConstructor(DungeonsXL.class, World.class, int.class, ConfigurationSection.class); } catch (NoSuchMethodException | SecurityException exception) { return null; } protection.worldName = worldName; protection.id = id; protection.config = config; protection.cache = plugin.getGlobalProtectionCache(); return protection; } public T load(World world) { if (!world.getName().equals(worldName)) { throw new IllegalArgumentException("World mismatch: Expected " + worldName + ", but received " + world); } T protection = null; try { protection = (T) constructor.newInstance(plugin, world, id, config); } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { MessageUtil.log(plugin, "Could not find or invoke " + constructor); exception.printStackTrace(); } if (protection != null) { cache.getUnloadedProtections().remove(this); } return protection; } @Override public String toString() { return "UnloadedProtection{type=" + constructor.getDeclaringClass().getName() + "; " + "world=" + worldName + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/mob/CitizensMobProvider.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.mob; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.mob.ExternalMobProvider; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.util.NumberUtil; import java.util.HashSet; import java.util.Set; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.event.NPCDeathEvent; import net.citizensnpcs.api.npc.AbstractNPC; import net.citizensnpcs.api.npc.NPC; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.entity.LivingEntity; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; /** * ExternalMobProvider implementation for Citizens. * * @author Daniel Saukel */ public class CitizensMobProvider implements ExternalMobProvider, Listener { private DungeonsAPI api; private static final String IDENTIFIER = "CI"; private DNPCRegistry registry = new DNPCRegistry(); private Set spawnedNPCs = new HashSet<>(); public CitizensMobProvider(DungeonsAPI api) { this.api = api; } /** * @return the DungeonsXL NPC registry */ public DNPCRegistry getNPCRegistry() { return registry; } /** * @return the spawned Citizens NPCs */ public Set getSpawnedNPCs() { return spawnedNPCs; } /** * @param npc the NPC to add */ public void addSpawnedNPC(NPC npc) { spawnedNPCs.add(npc); } /** * @param npc the NPC to remove */ public void removeSpawnedNPC(NPC npc) { spawnedNPCs.remove(npc); npc.destroy(); } public void removeSpawnedNPCs(World world) { Set worldNPCs = new HashSet<>(); for (NPC npc : spawnedNPCs) { if (npc.getStoredLocation().getWorld().equals(world)) { worldNPCs.add(npc); } } worldNPCs.forEach(this::removeSpawnedNPC); } @Override public String getIdentifier() { return IDENTIFIER; } @Override public String getRawCommand() { return null; } @Override public String getCommand(String mob, String world, double x, double y, double z) { return null; } @Override public void summon(String mob, Location location) { NPC source = CitizensAPI.getNPCRegistry().getById(NumberUtil.parseInt(mob)); if (!(source instanceof AbstractNPC)) { return; } GameWorld gameWorld = api.getGameWorld(location.getWorld()); if (gameWorld == null) { return; } boolean nativeRegistry = gameWorld.getDungeon().getRules().getState(GameRule.USE_NATIVE_CITIZENS_REGISTRY); NPC npc = nativeRegistry ? source.clone() : registry.createTransientClone((AbstractNPC) source); if (npc.isSpawned()) { npc.despawn(); } npc.spawn(location); spawnedNPCs.add(npc); api.wrapEntity((LivingEntity) npc.getEntity(), gameWorld, mob); } /* Listeners */ @EventHandler public void onNPCDeath(NPCDeathEvent event) { NPC npc = event.getNPC(); if (spawnedNPCs.contains(npc)) { CitizensAPI.getNPCRegistry().deregister(npc); removeSpawnedNPC(npc); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/mob/CustomExternalMobProvider.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.mob; import de.erethon.dungeonsxl.api.mob.ExternalMobProvider; import java.util.Map.Entry; /** * A custom external mob provider like defined in the main config file. * * @author Daniel Saukel */ public class CustomExternalMobProvider implements ExternalMobProvider { private String identifier; private String command; public CustomExternalMobProvider(String identifier, String command) { this.identifier = identifier; if (command.startsWith("/")) { command = command.replaceFirst("/", ""); } this.command = command; } public CustomExternalMobProvider(Entry entry) { this(entry.getKey(), (String) entry.getValue()); } @Override public String getIdentifier() { return identifier; } @Override public String getRawCommand() { return command; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/mob/DMob.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.mob; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.event.mob.DungeonMobDeathEvent; import de.erethon.dungeonsxl.api.event.mob.DungeonMobSpawnEvent; import de.erethon.dungeonsxl.api.mob.DungeonMob; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.trigger.MobTrigger; import de.erethon.dungeonsxl.trigger.WaveTrigger; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.mob.ExMob; import de.erethon.xlib.mob.VanillaMob; import java.util.Collection; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.entity.LivingEntity; import org.bukkit.event.entity.EntityDeathEvent; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class DMob implements DungeonMob { private LivingEntity entity; private ExMob type; private String trigger; public DMob(LivingEntity entity, GameWorld gameWorld, ExMob type, String trigger) { this.entity = entity; this.type = type != null ? type : VanillaMob.get(entity.getType()); if (this.type.getSpecies().isAlive() && this.type != VanillaMob.ARMOR_STAND && this.type != VanillaMob.PLAYER && !getDrops(gameWorld.getDungeon().getRules().getState(GameRule.MOB_ITEM_DROPS))) { entity.getEquipment().setHelmetDropChance(0); entity.getEquipment().setChestplateDropChance(0); entity.getEquipment().setLeggingsDropChance(0); entity.getEquipment().setBootsDropChance(0); if (Version.isAtLeast(Version.MC1_9)) { entity.getEquipment().setItemInMainHandDropChance(0); entity.getEquipment().setItemInOffHandDropChance(0); } else { entity.getEquipment().setItemInHandDropChance(0); } } DungeonMobSpawnEvent event = new DungeonMobSpawnEvent(this); Bukkit.getPluginManager().callEvent(event); this.trigger = trigger; gameWorld.addMob(this); } /* Getters */ @Override public LivingEntity getEntity() { return entity; } @Override public ExMob getType() { return type; } @Override public String getTriggerId() { return trigger; } /* Actions */ public void onDeath(DungeonsXL plugin, EntityDeathEvent event) { LivingEntity victim = event.getEntity(); DGameWorld gameWorld = (DGameWorld) plugin.getGameWorld(victim.getWorld()); if (gameWorld == null) { return; } DungeonMobDeathEvent dMobDeathEvent = new DungeonMobDeathEvent(this); Bukkit.getServer().getPluginManager().callEvent(dMobDeathEvent); if (dMobDeathEvent.isCancelled()) { return; } if (!getDrops(gameWorld.getDungeon().getRules().getState(GameRule.MOB_ITEM_DROPS))) { event.getDrops().clear(); } if (!getDrops(gameWorld.getDungeon().getRules().getState(GameRule.MOB_EXP_DROPS))) { event.setDroppedExp(0); } MobTrigger mobTrigger = MobTrigger.getByName(trigger, gameWorld); if (mobTrigger != null) { mobTrigger.trigger(true, null); } Collection waveTriggers = gameWorld.getTriggersFromKey(TriggerTypeKey.WAVE); for (Trigger uncasted : waveTriggers) { WaveTrigger waveTrigger = (WaveTrigger) uncasted; if (((DGame) gameWorld.getGame()).getWaveKills() >= Math.ceil(gameWorld.getMobCount() * waveTrigger.getMustKillRate())) { waveTrigger.trigger(true, null); } } gameWorld.removeMob(this); } private boolean getDrops(Object drops) { if (drops instanceof Boolean) { return (Boolean) drops; } else if (drops instanceof Set) { for (ExMob whitelisted : (Set) drops) { if (type.isSubsumableUnder(whitelisted)) { return true; } } } return false; } @Override public String toString() { return getClass().getSimpleName() + "{type=" + type + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/mob/DMobListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.mob; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.xlib.mob.VanillaMob; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.PiglinAbstract; import org.bukkit.entity.Skeleton; import org.bukkit.entity.Zombie; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import org.bukkit.event.entity.EntityCombustByEntityEvent; import org.bukkit.event.entity.EntityCombustEvent; import org.bukkit.event.entity.EntityDeathEvent; /** * @author Daniel Saukel, Frank Baumann */ public class DMobListener implements Listener { private DungeonsXL plugin; public DMobListener(DungeonsXL plugin) { this.plugin = plugin; } @EventHandler public void onCreatureSpawn(CreatureSpawnEvent event) { World world = event.getLocation().getWorld(); InstanceWorld instance = plugin.getInstanceWorld(world); if (instance == null) { return; } switch (event.getSpawnReason()) { case CHUNK_GEN: case JOCKEY: case MOUNT: case NATURAL: event.setCancelled(true); return; } VanillaMob vm = VanillaMob.get(event.getEntityType()); if (vm == VanillaMob.PIGLIN || vm == VanillaMob.PIGLIN_BRUTE) { ((PiglinAbstract) event.getEntity()).setImmuneToZombification(true); } } @EventHandler public void onEntityDeath(EntityDeathEvent event) { World world = event.getEntity().getWorld(); if (event.getEntity() instanceof LivingEntity) { LivingEntity entity = event.getEntity(); GameWorld gameWorld = plugin.getGameWorld(world); if (gameWorld != null) { if (gameWorld.isPlaying()) { DMob dMob = (DMob) plugin.getDungeonMob(entity); if (dMob != null) { dMob.onDeath(plugin, event); } } } } } // Prevent undead combustion from the sun. @EventHandler public void onEntityCombust(EntityCombustEvent event) { if (event instanceof EntityCombustByEntityEvent) { return; } Entity entity = event.getEntity(); if ((entity instanceof Skeleton || entity instanceof Zombie) && plugin.getGameWorld(entity.getWorld()) != null) { event.setCancelled(true); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/mob/DNPCRegistry.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.mob; import java.util.Iterator; import java.util.UUID; import net.citizensnpcs.Settings.Setting; import net.citizensnpcs.api.CitizensAPI; import net.citizensnpcs.api.event.NPCCreateEvent; import net.citizensnpcs.api.npc.AbstractNPC; import net.citizensnpcs.api.npc.NPC; import net.citizensnpcs.api.npc.NPCRegistry; import net.citizensnpcs.api.persistence.PersistenceLoader; import net.citizensnpcs.api.trait.Trait; import net.citizensnpcs.api.trait.trait.MobType; import net.citizensnpcs.api.util.DataKey; import net.citizensnpcs.api.util.MemoryDataKey; import net.citizensnpcs.npc.CitizensNPC; import net.citizensnpcs.npc.EntityControllers; import net.citizensnpcs.trait.ArmorStandTrait; import net.citizensnpcs.trait.LookClose; import net.citizensnpcs.trait.MountTrait; import org.bukkit.Bukkit; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; /** * This class folllows the implementation in Citizens, see * GitHub * * @author Daniel Saukel */ public class DNPCRegistry implements NPCRegistry { @Override public NPC createNPC(EntityType type, String name) { return createNPC(type, UUID.randomUUID(), 0, name); } @Override public NPC createNPC(EntityType type, UUID uuid, int id, String name) { NPC npc = new CitizensNPC(uuid, id, name, EntityControllers.createForType(type), this); if (npc == null) { throw new IllegalStateException("Could not create NPC: npc is null"); } Bukkit.getPluginManager().callEvent(new NPCCreateEvent(npc)); if (type == EntityType.ARMOR_STAND && !npc.hasTrait(ArmorStandTrait.class)) { npc.addTrait(ArmorStandTrait.class); } if (Setting.DEFAULT_LOOK_CLOSE.asBoolean()) { npc.addTrait(LookClose.class); } npc.addTrait(MountTrait.class); return npc; } @Override public void deregister(NPC npc) { CitizensAPI.getNPCRegistry().deregister(npc); } @Override public void deregisterAll() { CitizensAPI.getNPCRegistry().deregisterAll(); } @Override public NPC getById(int id) { return CitizensAPI.getNPCRegistry().getById(id); } @Override public NPC getByUniqueId(UUID uuid) { return CitizensAPI.getNPCRegistry().getByUniqueId(uuid); } @Override public NPC getByUniqueIdGlobal(UUID uuid) { return CitizensAPI.getNPCRegistry().getByUniqueIdGlobal(uuid); } @Override public NPC getNPC(Entity entity) { return CitizensAPI.getNPCRegistry().getNPC(entity); } @Override public boolean isNPC(Entity entity) { return CitizensAPI.getNPCRegistry().isNPC(entity); } @Override public Iterable sorted() { return CitizensAPI.getNPCRegistry().sorted(); } @Override public Iterator iterator() { return CitizensAPI.getNPCRegistry().iterator(); } /** * Clones an NPC without spamming the config. * * @param npc the NPC to clone * @return a clone of the NPC */ public NPC createTransientClone(AbstractNPC npc) { NPC copy = createNPC(npc.getTrait(MobType.class).getType(), npc.getFullName()); DataKey key = new MemoryDataKey(); save(npc, key); copy.load(key); for (Trait trait : copy.getTraits()) { trait.onCopy(); } return copy; } // Like in AbstractNPC#save(DataKey), but without persistence stuff public void save(AbstractNPC npc, DataKey root) { if (!npc.data().get(NPC.SHOULD_SAVE_METADATA, true)) { return; } npc.data().saveTo(root.getRelative("metadata")); root.setString("name", npc.getFullName()); root.setString("uuid", npc.getUniqueId().toString()); StringBuilder traitNames = new StringBuilder(); for (Trait trait : npc.getTraits()) { DataKey traitKey = root.getRelative("traits." + trait.getName()); trait.save(traitKey); PersistenceLoader.save(trait, traitKey); //npc.removedTraits.remove(trait.getName()); traitNames.append(trait.getName() + ","); } if (traitNames.length() > 0) { root.setString("traitnames", traitNames.substring(0, traitNames.length() - 1)); } else { root.setString("traitnames", ""); } //npc.removedTraits.clear(); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/mob/ExternalMobPlugin.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.mob; import de.erethon.dungeonsxl.api.mob.ExternalMobProvider; /** * Officially supported external mob plugins. * * @author Daniel Saukel */ public enum ExternalMobPlugin implements ExternalMobProvider { CUSTOM_MOBS("CM", "ccmob spawn %mob% %world% %block_x% %block_y% %block_z%"), INSANE_MOBS("IM", "insanemobs %mob% %x% %y% %z% %world%"), MYTHIC_MOBS("MM", "mythicmobs mobs spawn -s %mob% 1 %world%,%x%,%y%,%z%"); private String identifier; private String command; ExternalMobPlugin(String identifier, String command) { this.identifier = identifier; this.command = command; } @Override public String getIdentifier() { return identifier; } @Override public String getRawCommand() { return command; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DEditPlayer.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.EditPlayer; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.ProgressBar; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class DEditPlayer extends DInstancePlayer implements EditPlayer { private String[] linesCopy; private EditWorld editWorld; public DEditPlayer(DungeonsXL plugin, Player player, EditWorld world) { super(plugin, player, world); editWorld = world; // Set gamemode a few ticks later to avoid incompatibilities with plugins that force a gamemode new BukkitRunnable() { @Override public void run() { player.setGameMode(GameMode.CREATIVE); } }.runTaskLater(plugin, 10L); clearPlayerData(); Location teleport = world.getLobbyLocation(); if (teleport == null) { player.teleport(world.getWorld().getSpawnLocation()); } else { player.teleport(teleport); } // Permission bridge if (xlib.getPermissionProvider() != null) { for (String permission : plugin.getMainConfig().getEditPermissions()) { xlib.getPermissionProvider().playerAddTransient(getWorld().getName(), player, permission); } } } /* Getters and setters */ @Override public EditWorld getEditWorld() { return editWorld; } @Override public String[] getCopiedLines() { return linesCopy; } @Override public void setCopiedLines(String[] linesCopy) { this.linesCopy = linesCopy; } /* Actions */ @Override public void delete() { // Permission bridge if (xlib.getPermissionProvider() != null) { for (String permission : plugin.getMainConfig().getEditPermissions()) { xlib.getPermissionProvider().playerRemoveTransient(getWorld().getName(), player, permission); } } super.delete(); } /** * Escape the DEditWorld without saving. */ @Override public void escape() { delete(); reset(false); } public void poke(Block block) { if (block.getState() instanceof Sign) { Sign sign = (Sign) block.getState(); String[] lines = sign.getLines(); if (lines[0].isEmpty() && lines[1].isEmpty() && lines[2].isEmpty() && lines[3].isEmpty()) { if (linesCopy != null) { SignChangeEvent event = new SignChangeEvent(block, getPlayer(), linesCopy); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { sign.setLine(0, event.getLine(0)); sign.setLine(1, event.getLine(1)); sign.setLine(2, event.getLine(2)); sign.setLine(3, event.getLine(3)); sign.update(); } } } else { linesCopy = lines; MessageUtil.sendMessage(getPlayer(), DMessage.PLAYER_SIGN_COPIED.getMessage()); } } else { String info = "" + block.getType(); if (block.getData() != 0) { info = info + "," + block.getData(); } MessageUtil.sendMessage(getPlayer(), DMessage.PLAYER_BLOCK_INFO.getMessage(info)); } } @Override public void leave(boolean unloadIfEmpty) { delete(); reset(false); if (unloadIfEmpty && !plugin.isLoadingWorld() && editWorld != null && editWorld.getPlayers().isEmpty()) { plugin.setLoadingWorld(true); editWorld.delete(); new ProgressBar(player, config.getEditInstanceRemovalDelay()) { @Override public void onFinish() { MessageUtil.sendActionBarMessage(player, DMessage.CMD_SAVE_SUCCESS.getMessage()); plugin.setLoadingWorld(false); } }.send(plugin); } } @Override public void update() { if (player.getWorld().equals(getWorld())) { return; } MessageUtil.debug(plugin, "Player " + this + " was expected to be in " + getWorld() + " but is in " + player.getWorld()); Location teleportLocation; if (editWorld.getLobbyLocation() != null) { teleportLocation = editWorld.getLobbyLocation(); } else { teleportLocation = getWorld().getSpawnLocation(); } MessageUtil.debug(plugin, "Teleporting to " + teleportLocation); player.teleport(teleportLocation); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DGamePlayer.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameGoal; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.event.group.GroupPlayerKickEvent; import de.erethon.dungeonsxl.api.event.group.GroupScoreEvent; import de.erethon.dungeonsxl.api.event.player.GamePlayerDeathEvent; import de.erethon.dungeonsxl.api.event.player.GamePlayerFinishEvent; import de.erethon.dungeonsxl.api.event.player.GlobalPlayerRewardPayOutEvent; import de.erethon.dungeonsxl.api.event.requirement.RequirementDemandEvent; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerClass; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.trigger.DistanceTrigger; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.dungeonsxl.world.block.TeamFlag; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.mob.VanillaMob; import java.util.ArrayList; import java.util.List; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.entity.Damageable; import org.bukkit.entity.EntityType; import org.bukkit.entity.ExperienceOrb; import org.bukkit.entity.Player; import org.bukkit.entity.Wolf; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Tobias Schmitz, Milan Albrecht, Daniel Saukel */ public class DGamePlayer extends DInstancePlayer implements GamePlayer { private DGroup dGroup; private boolean ready = false; private boolean finished = false; private PlayerClass dClass; private Location checkpoint; private Wolf wolf; private int wolfRespawnTime = 30; private long offlineTime; private boolean resetClassInventoryOnRespawn; private int initialLives = -1; private int lives; private ItemStack oldHelmet; private PlayerGroup stealing; private DGroupTag groupTag; public DGamePlayer(DungeonsXL plugin, Player player, GameWorld world) { super(plugin, player, world); Game game = world.getGame(); dGroup = (DGroup) plugin.getPlayerGroup(player); GameRuleContainer rules = game.getRules(); player.setGameMode(GameMode.SURVIVAL); if (!rules.getState(GameRule.KEEP_INVENTORY_ON_ENTER)) { clearPlayerData(); } player.setAllowFlight(rules.getState(GameRule.FLY)); initialLives = rules.getState(GameRule.INITIAL_LIVES); lives = initialLives; resetClassInventoryOnRespawn = rules.getState(GameRule.RESET_CLASS_INVENTORY_ON_RESPAWN); if (rules.getState(GameRule.GROUP_TAG_ENABLED)) { initDGroupTag(); } Location teleport = world.getLobbyLocation(); if (teleport == null) { player.teleport(world.getWorld().getSpawnLocation()); } else { player.teleport(teleport); } if (!((DGameWorld) world).hasReadySign()) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_READY_SIGN.getMessage(world.getName())); } } /* Getters and setters */ @Override public String getName() { String name = player.getName(); if (getGroup() != null && dGroup.getDColor() != null) { name = getGroup().getDColor().getChatColor() + name; } return name; } @Override public DGroup getGroup() { return dGroup; } public void setPlayer(Player player) { this.player = player; } /** * @return if the player is in test mode */ public boolean isInTestMode() { if (getGroup() == null) { return false; } if (getGame() == null) { return false; } return getGame().hasRewards(); } @Override public boolean isReady() { return ready; } public void setReady(boolean ready) { this.ready = ready; } @Override public boolean isFinished() { return finished; } @Override public void setFinished(boolean finished) { this.finished = finished; } @Override public PlayerClass getPlayerClass() { return dClass; } @Override public void setPlayerClass(PlayerClass playerClass) { dClass = playerClass; if (dClass == null) { return; } /* Set Dog */ if (wolf != null) { wolf.remove(); wolf = null; } if (dClass.hasDog()) { wolf = (Wolf) getWorld().spawnEntity(getPlayer().getLocation(), EntityType.WOLF); wolf.setTamed(true); wolf.setOwner(getPlayer()); double maxHealth = ((Damageable) wolf).getMaxHealth(); wolf.setHealth(maxHealth); } /* Delete Inventory */ getPlayer().getInventory().clear(); getPlayer().getInventory().setArmorContents(null); getPlayer().getInventory().setItemInHand(VanillaItem.AIR.toItemStack()); // Remove Potion Effects for (PotionEffect effect : getPlayer().getActivePotionEffects()) { getPlayer().removePotionEffect(effect.getType()); } // Reset lvl getPlayer().setExp(0f); getPlayer().setLevel(0); /* Set Inventory */ for (ItemStack istack : dClass.getItems()) { // Leggings if (VanillaItem.LEATHER_LEGGINGS.is(istack) || VanillaItem.CHAINMAIL_LEGGINGS.is(istack) || VanillaItem.IRON_LEGGINGS.is(istack) || VanillaItem.DIAMOND_LEGGINGS.is(istack) || VanillaItem.GOLDEN_LEGGINGS.is(istack)) { getPlayer().getInventory().setLeggings(istack); } // Helmet else if (VanillaItem.LEATHER_HELMET.is(istack) || VanillaItem.CHAINMAIL_HELMET.is(istack) || VanillaItem.IRON_HELMET.is(istack) || VanillaItem.DIAMOND_HELMET.is(istack) || VanillaItem.GOLDEN_HELMET.is(istack)) { getPlayer().getInventory().setHelmet(istack); } // Chestplate else if (VanillaItem.LEATHER_CHESTPLATE.is(istack) || VanillaItem.CHAINMAIL_CHESTPLATE.is(istack) || VanillaItem.IRON_CHESTPLATE.is(istack) || VanillaItem.DIAMOND_CHESTPLATE.is(istack) || VanillaItem.GOLDEN_CHESTPLATE.is(istack)) { getPlayer().getInventory().setChestplate(istack); } // Boots else if (VanillaItem.LEATHER_BOOTS.is(istack) || VanillaItem.CHAINMAIL_BOOTS.is(istack) || VanillaItem.IRON_BOOTS.is(istack) || VanillaItem.DIAMOND_BOOTS.is(istack) || VanillaItem.GOLDEN_BOOTS.is(istack)) { getPlayer().getInventory().setBoots(istack); } else { getPlayer().getInventory().addItem(istack); } } } @Override public Location getLastCheckpoint() { return checkpoint; } @Override public void setLastCheckpoint(Location checkpoint) { this.checkpoint = checkpoint; } @Override public long getOfflineTimeMillis() { return offlineTime; } @Override public void setOfflineTimeMillis(long offlineTime) { this.offlineTime = offlineTime; } @Override public int getInitialLives() { return initialLives; } @Override public void setInitialLives(int initialLives) { this.initialLives = initialLives; } @Override public int getLives() { return lives; } @Override public void setLives(int lives) { this.lives = lives; } @Override public Wolf getWolf() { return wolf; } @Override public void setWolf(Wolf wolf) { this.wolf = wolf; } public int getWolfRespawnTime() { return wolfRespawnTime; } public void setWolfRespawnTime(int wolfRespawnTime) { this.wolfRespawnTime = wolfRespawnTime; } @Override public boolean isStealingFlag() { return stealing != null; } @Override public PlayerGroup getRobbedGroup() { return stealing; } @Override public void setRobbedGroup(PlayerGroup group) { if (group != null) { oldHelmet = player.getInventory().getHelmet(); player.getInventory().setHelmet(getGroup().getDColor().getWoolMaterial().toItemStack()); } stealing = group; } public DGroupTag getDGroupTag() { return groupTag; } public void initDGroupTag() { groupTag = new DGroupTag(plugin, this); } /* Actions */ @Override public void captureFlag() { if (stealing == null) { return; } Game game = plugin.getGame(getWorld()); if (game == null) { return; } GroupScoreEvent event = new GroupScoreEvent(getGroup(), this, stealing); if (event.isCancelled()) { return; } game.sendMessage(DMessage.GROUP_FLAG_CAPTURED.getMessage(getName(), stealing.getName())); GameRuleContainer rules = game.getRules(); getGroup().setScore(getGroup().getScore() + 1); GameGoal goal = rules.getState(GameRule.GAME_GOAL); if (goal.getType().hasComponent(GameGoal.SCORE_GOAL) && goal.getState(GameGoal.SCORE_GOAL) == getGroup().getScore()) { getGroup().winGame(); } stealing.setScore(stealing.getScore() - 1); if (stealing.getScore() == -1) { for (GamePlayer member : ((DGroup) stealing).getDGamePlayers()) { member.kill(); } game.sendMessage(DMessage.GROUP_DEFEATED.getMessage(stealing.getName())); } stealing = null; player.getInventory().setHelmet(oldHelmet); if (game.getGroups().size() == 1) { dGroup.winGame(); } } @Override public void leave() { leave(true); } @Override public void leave(boolean message) { if (getGame() == null) { return; } MessageUtil.assertTrue("GameWorld must not be null", getGameWorld(), w -> w != null); GameRuleContainer rules = getGame().getRules(); delete(); if (player.isOnline()) { reset(finished); } // Permission bridge if (xlib.getPermissionProvider() != null) { for (String permission : rules.getState(GameRule.GAME_PERMISSIONS)) { xlib.getPermissionProvider().playerRemoveTransient(getWorld().getName(), player, permission); } } if (getGroup() != null) { dGroup.removeMember(getPlayer(), message); } if (getGame() != null && finished && getGame().hasRewards()) { MessageUtil.debug(plugin, "Rewarding " + this); reward(); } if (getGroup() != null) { if (!dGroup.isEmpty()) { /*if (dGroup.finishIfMembersFinished()) { return; }*/ // Give secure objects to other players Player groupPlayer = null; for (Player player : dGroup.getMembers().getOnlinePlayers()) { if (player.isOnline()) { groupPlayer = player; break; } } if (groupPlayer != null) { for (ItemStack itemStack : getPlayer().getInventory()) { if (itemStack != null) { if (((DGameWorld) getGameWorld()).getSecureObjects().contains(itemStack)) { groupPlayer.getInventory().addItem(itemStack); } } } } } if (dGroup.getLeader().equals(getPlayer()) && dGroup.getMembers().size() > 0) { // Captain here! Player newCaptain = null; for (Player player : dGroup.getMembers().getOnlinePlayers()) { if (player.isOnline()) { newCaptain = player; break; } } dGroup.setLeader(newCaptain); if (message) { MessageUtil.sendMessage(newCaptain, DMessage.PLAYER_NEW_LEADER.getMessage()); } // ...*flies away* } } if (getGameWorld().getPlayers().isEmpty()) { getGameWorld().delete(); } } public void reward() { List rewards = new ArrayList<>(dGroup.getRewards()); rewards.addAll(getGame().getRules().getState(GameRule.REWARDS)); GlobalPlayerRewardPayOutEvent globalPlayerPayOutRewardEvent = new GlobalPlayerRewardPayOutEvent(this, rewards); Bukkit.getPluginManager().callEvent(globalPlayerPayOutRewardEvent); if (!globalPlayerPayOutRewardEvent.isCancelled()) { giveLoot(getGroup().getDungeon(), globalPlayerPayOutRewardEvent.getRewards()); } getData().logTimeLastFinished(getGroup().getDungeonName()); // Tutorial Permissions if (!getGame().isTutorial()) { return; } getData().setFinishedTutorial(true); if (xlib.getPermissionProvider() != null && xlib.getPermissionProvider().hasGroupSupport()) { String endGroup = plugin.getMainConfig().getTutorialEndGroup(); if (xlib.isGroupEnabled(endGroup)) { xlib.getPermissionProvider().playerAddGroup(getPlayer(), endGroup); } String startGroup = plugin.getMainConfig().getTutorialStartGroup(); if (xlib.isGroupEnabled(startGroup)) { xlib.getPermissionProvider().playerRemoveGroup(getPlayer(), startGroup); } } } @Override public void kill() { GroupPlayerKickEvent dPlayerKickEvent = new GroupPlayerKickEvent(getGroup(), this, GroupPlayerKickEvent.Cause.DEATH); Bukkit.getPluginManager().callEvent(dPlayerKickEvent); if (!dPlayerKickEvent.isCancelled()) { GameWorld gameWorld = getGroup().getGameWorld(); if (lives != -1) { gameWorld.sendMessage(DMessage.PLAYER_DEATH_KICK.getMessage(getName())); } else if (getGroup().getLives() != -1) { gameWorld.sendMessage(DMessage.GROUP_DEATH_KICK.getMessage(getName(), dGroup.getName())); } leave(); } } @Override public boolean ready() { if (dGroup == null) { return false; } Game game = dGroup.getGame(); if (game == null) { game = new DGame(plugin, dGroup.getDungeon(), dGroup); } if (game.hasRewards() && !checkRequirements(game.getDungeon())) { return false; } ready = true; return ready; } @Override public void respawn() { Location respawn = checkpoint; if (respawn == null) { respawn = getGroup().getGameWorld().getStartLocation(dGroup); } if (respawn == null) { respawn = getWorld().getSpawnLocation(); } getPlayer().teleport(respawn); if (resetClassInventoryOnRespawn) { setPlayerClass(dClass); } else { // Don't forget Doge! if (wolf != null) { wolf.teleport(getPlayer()); } } } public void startGame() { Dungeon dungeon = getGame().getDungeon(); GameRuleContainer rules = dungeon.getRules(); getData().logTimeLastStarted(dungeon.getName()); getData().setKeepInventoryAfterLogout(rules.getState(GameRule.KEEP_INVENTORY_ON_ESCAPE)); respawn(); if (plugin.getMainConfig().isSendFloorTitleEnabled()) { String mapName = getGameWorld().getName(); if (rules.getState(GameRule.TITLE) != null || rules.getState(GameRule.SUBTITLE) != null) { String title = rules.getState(GameRule.TITLE) == null ? "" : rules.getState(GameRule.TITLE); String subtitle = rules.getState(GameRule.SUBTITLE) == null ? "" : rules.getState(GameRule.SUBTITLE); MessageUtil.sendTitleMessage(player, title, subtitle, rules.getState(GameRule.TITLE_FADE_IN), rules.getState(GameRule.TITLE_SHOW), rules.getState(GameRule.TITLE_FADE_OUT)); } else if (!dungeon.getName().equals(mapName)) { MessageUtil.sendTitleMessage(player, "&b&l" + dungeon.getName().replaceAll("_", " "), "&4&l" + mapName.replaceAll("_", " ")); } else { MessageUtil.sendTitleMessage(player, "&4&l" + mapName.replaceAll("_", " ")); } if (rules.getState(GameRule.ACTION_BAR) != null) { MessageUtil.sendActionBarMessage(player, rules.getState(GameRule.ACTION_BAR)); } if (rules.getState(GameRule.CHAT) != null) { MessageUtil.sendCenteredMessage(player, rules.getState(GameRule.CHAT)); } } for (Requirement requirement : rules.getState(GameRule.REQUIREMENTS)) { RequirementDemandEvent requirementDemandEvent = new RequirementDemandEvent(requirement, dungeon, player, rules.getState(GameRule.KEEP_INVENTORY_ON_ENTER)); Bukkit.getPluginManager().callEvent(requirementDemandEvent); if (requirementDemandEvent.isCancelled()) { continue; } if (!DPermission.hasPermission(player, DPermission.IGNORE_REQUIREMENTS)) { requirement.demand(player); } } player.setGameMode(rules.getState(GameRule.GAME_MODE)); // Permission bridge if (xlib.getPermissionProvider() != null) { for (String permission : rules.getState(GameRule.GAME_PERMISSIONS)) { xlib.getPermissionProvider().playerAddTransient(getGame().getWorld().getWorld().getName(), player, permission); } } } /** * The DGamePlayer finishs the current floor. * * @param specifiedFloor the name of the next floor */ public void finishFloor(DResourceWorld specifiedFloor) { if (!dGroup.getDungeon().isMultiFloor()) { finish(); return; } MessageUtil.sendMessage(getPlayer(), DMessage.PLAYER_FINISHED_FLOOR.getMessage()); finished = true; boolean hasToWait = false; if (getGroup() == null) { return; } if (!dGroup.isPlaying()) { return; } getGame().setNextFloor(specifiedFloor); if (dGroup.isFinished()) { dGroup.finishFloor(specifiedFloor); } else { MessageUtil.sendMessage(player, DMessage.PLAYER_WAIT_FOR_OTHER_PLAYERS.getMessage()); hasToWait = true; } GamePlayerFinishEvent gamePlayerFinishEvent = new GamePlayerFinishEvent(this, hasToWait); Bukkit.getPluginManager().callEvent(gamePlayerFinishEvent); if (gamePlayerFinishEvent.isCancelled()) { finished = false; } } @Override public void finish() { finish(true); } @Override public void finish(boolean message) { if (message) { MessageUtil.sendMessage(getPlayer(), DMessage.PLAYER_FINISHED_DUNGEON.getMessage()); } finished = true; boolean hasToWait = false; if (!getGroup().isPlaying()) { return; } if (dGroup.isFinished()) { dGroup.finish(); } else { if (message) { MessageUtil.sendMessage(this.getPlayer(), DMessage.PLAYER_WAIT_FOR_OTHER_PLAYERS.getMessage()); } hasToWait = true; } GamePlayerFinishEvent gamePlayerFinishEvent = new GamePlayerFinishEvent(this, hasToWait); Bukkit.getPluginManager().callEvent(gamePlayerFinishEvent); if (gamePlayerFinishEvent.isCancelled()) { finished = false; } } public void onDeath(PlayerDeathEvent event) { DGameWorld gameWorld = (DGameWorld) getGameWorld(); if (gameWorld == null) { return; } DGame game = (DGame) getGame(); if (game == null) { return; } GameRuleContainer rules = game.getRules(); boolean keepInventory = rules.getState(GameRule.KEEP_INVENTORY_ON_DEATH); GamePlayerDeathEvent dPlayerDeathEvent = new GamePlayerDeathEvent(this, keepInventory, 1); Bukkit.getPluginManager().callEvent(dPlayerDeathEvent); if (dPlayerDeathEvent.isCancelled()) { return; } Location deathLocation = player.getLocation(); if (keepInventory && event != null) { event.setKeepInventory(true); event.getDrops().clear(); event.setKeepLevel(true); event.setDroppedExp(0); } else if (!keepInventory && event == null) { for (ItemStack itemStack : player.getInventory().getContents()) { if (itemStack == null) { continue; } getWorld().dropItemNaturally(deathLocation, itemStack); } player.getInventory().clear(); ExperienceOrb orb = (ExperienceOrb) VanillaMob.EXPERIENCE_ORB.toEntity(deathLocation); int expDrop = 7 * player.getLevel(); expDrop = expDrop > 100 ? 100 : expDrop; orb.setExperience(expDrop); int newLevel = player.getLevel() - 7; player.setLevel(newLevel > 0 ? newLevel : 0); player.setExp(0); } if (event == null) { Location respawn = getLastCheckpoint(); if (respawn == null) { respawn = gameWorld.getStartLocation(getGroup()); } player.teleport(respawn); heal(); if (getWolf() != null) { getWolf().teleport(respawn); } if (rules.getState(GameRule.RESET_CLASS_INVENTORY_ON_RESPAWN)) { setPlayerClass(dClass); } } else if (config.areGlobalDeathMessagesDisabled()) { event.setDeathMessage(null); } if (getGroup() != null && dGroup.getLives() != -1) { int newLives = dGroup.getLives() - dPlayerDeathEvent.getLostLives(); dGroup.setLives(newLives < 0 ? 0 : newLives);// If the group already has 0 lives, don't remove any gameWorld.sendMessage(DMessage.GROUP_DEATH.getMessage(getName(), dGroup.getName(), String.valueOf(dGroup.getLives()))); } else { if (lives != -1) { lives = lives - dPlayerDeathEvent.getLostLives(); } GamePlayer killer = plugin.getPlayerCache().getGamePlayer(player.getKiller()); String newLives = lives == -1 ? DMessage.PLAYER_UNLIMITED_LIVES.getMessage() : String.valueOf(this.lives); if (killer != null) { gameWorld.sendMessage(DMessage.PLAYER_KILLED.getMessage(getName(), killer.getName(), newLives)); } else { gameWorld.sendMessage(DMessage.PLAYER_DEATH.getMessage(getName(), newLives)); } } if (isStealingFlag()) { for (TeamFlag teamFlag : gameWorld.getTeamFlags()) { if (teamFlag.getOwner().equals(stealing)) { teamFlag.reset(); gameWorld.sendMessage(DMessage.GROUP_FLAG_LOST.getMessage(player.getName(), stealing.getName())); stealing = null; } } } if ((dGroup.getLives() == 0 || lives == 0) && ready) { new BukkitRunnable() { @Override public void run() { kill(); } }.runTaskLater(plugin, 1L); } if (rules.getState(GameRule.GAME_GOAL).getType() == GameGoal.Type.LAST_MAN_STANDING) { if (game.getGroups().size() == 1) { ((DGroup) game.getGroups().get(0)).winGame(); } } } @Override public void update() { if (player.isOnline()) { if (!player.getWorld().equals(getWorld())) { MessageUtil.debug(plugin, "Player " + this + " was expected to be in " + getWorld() + " but is in " + player.getWorld()); Location teleportLocation = getLastCheckpoint(); if (teleportLocation == null) { teleportLocation = getGameWorld().getStartLocation(getGroup()); } player.teleport(teleportLocation); if (wolf != null && !wolf.isDead()) { wolf.teleport(teleportLocation); } } if (wolf != null && wolf.isDead()) { if (getWolfRespawnTime() <= 0) { wolf = (Wolf) getWorld().spawnEntity(player.getLocation(), EntityType.WOLF); wolf.setTamed(true); wolf.setOwner(player); setWolfRespawnTime(30); } else { wolfRespawnTime--; } } } // Kick offline players if (getOfflineTimeMillis() > 0 && getOfflineTimeMillis() < System.currentTimeMillis()) { GroupPlayerKickEvent groupPlayerKickEvent = new GroupPlayerKickEvent(getGroup(), this, GroupPlayerKickEvent.Cause.OFFLINE); Bukkit.getPluginManager().callEvent(groupPlayerKickEvent); if (!groupPlayerKickEvent.isCancelled()) { MessageUtil.debug(plugin, this + " was kicked from their group for being offline for too long."); leave(); return; } } MessageUtil.assertTrue("Player " + this + " was expected to be online but is offline.", player, Player::isOnline); DistanceTrigger.triggerAllInDistance(player, (DGameWorld) getGameWorld()); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent.Cause; import de.erethon.dungeonsxl.api.event.requirement.RequirementCheckEvent; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.global.DPortal; import de.erethon.dungeonsxl.util.AttributeUtil; import de.erethon.dungeonsxl.util.LocationString; import de.erethon.xlib.XLib; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.UUID; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeModifier; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class DGlobalPlayer implements GlobalPlayer { protected DungeonsXL plugin; protected XLib xlib; protected boolean is1_9 = Version.isAtLeast(Version.MC1_9); protected Player player; private DPlayerData data; private boolean breakMode = false; private boolean groupChat = false; private boolean chatSpyMode = false; private DPortal creatingPortal; private ItemStack cachedItem; private boolean announcerEnabled = true; private List rewardItems; public DGlobalPlayer(DungeonsXL plugin, Player player) { this(plugin, player, false); } public DGlobalPlayer(DungeonsXL plugin, Player player, boolean reset) { this.plugin = plugin; xlib = plugin.getXLib(); this.player = player; loadPlayerData(new File(DungeonsXL.PLAYERS, player.getUniqueId().toString() + ".yml")); if (reset && data.wasInGame()) { reset(false); } plugin.getPlayerCache().add(player, this); } public DGlobalPlayer(DGlobalPlayer dPlayer) { plugin = dPlayer.plugin; player = dPlayer.getPlayer(); data = dPlayer.getData(); breakMode = dPlayer.isInBreakMode(); chatSpyMode = dPlayer.isInChatSpyMode(); creatingPortal = dPlayer.getPortal(); announcerEnabled = dPlayer.isAnnouncerEnabled(); plugin.getPlayerCache().add(player, this); } /* Getters and setters */ @Override public String getName() { return player.getName(); } @Override public Player getPlayer() { return player; } @Override public UUID getUniqueId() { return player.getUniqueId(); } /** * Returns the saved data. * * @return the saved data */ public DPlayerData getData() { return data; } /** * Load / reload a new instance of DPlayerData. * * @param file the file to load from */ public void loadPlayerData(File file) { data = new DPlayerData(file); } @Override public PlayerGroup getGroup() { return plugin.getPlayerGroup(player); } @Override public boolean isInGroupChat() { if (!plugin.getMainConfig().isChatEnabled()) { return false; } return groupChat; } @Override public void setInGroupChat(boolean groupChat) { this.groupChat = groupChat; } @Override public boolean isInChatSpyMode() { if (!plugin.getMainConfig().isChatEnabled()) { return false; } return chatSpyMode; } @Override public void setInChatSpyMode(boolean chatSpyMode) { this.chatSpyMode = chatSpyMode; } @Override public boolean isInBreakMode() { return breakMode; } @Override public void setInBreakMode(boolean breakMode) { this.breakMode = breakMode; } /** * Returns if the player is creating a DPortal. * * @return if the player is creating a DPortal */ public boolean isCreatingPortal() { return creatingPortal != null; } /** * Returns the portal the player is creating. * * @return the portal the player is creating */ public DPortal getPortal() { return creatingPortal; } /** * Sets the portal the player is creating. * * @param dPortal the portal to create */ public void setCreatingPortal(DPortal dPortal) { creatingPortal = dPortal; } /** * Returns the item the player had in his hand before he started to create a portal. * * @return the item the player had in his hand before he started to create a portal */ public ItemStack getCachedItem() { return cachedItem; } /** * Sets the cached item. * * @param item the cached item to set */ public void setCachedItem(ItemStack item) { cachedItem = item; } /** * Returns if the player has announcers enabled. * * @return if the players receives announcer messages */ public boolean isAnnouncerEnabled() { return announcerEnabled; } /** * Sets if the player has announcers enabled. * * @param enabled set if the players receives announcer messages */ public void setAnnouncerEnabled(boolean enabled) { announcerEnabled = enabled; } @Override public List getRewardItems() { return rewardItems; } @Override public boolean hasRewardItemsLeft() { return rewardItems != null; } @Override public void setRewardItems(List rewardItems) { this.rewardItems = rewardItems; } @Override public boolean hasPermission(String permission) { return player.hasPermission(permission); } public boolean hasPermission(DPermission permission) { return DPermission.hasPermission(player, permission); } @Override public boolean checkRequirements(Dungeon dungeon) { boolean fulfilled = true; GameRuleContainer rules = dungeon.getRules(); List msgs = new ArrayList<>(rules.getState(GameRule.REQUIREMENTS).size()); for (Requirement requirement : rules.getState(GameRule.REQUIREMENTS)) { if (requirement == null) { MessageUtil.log(plugin, "The dungeon " + dungeon.getName() + " has an invalid requirement."); continue; } RequirementCheckEvent event = new RequirementCheckEvent(requirement, dungeon, player, rules.getState(GameRule.KEEP_INVENTORY_ON_ENTER)); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { continue; } msgs.add(event.getCheckMessage()); if (!requirement.check(player)) { fulfilled = false; } } if (!fulfilled) { MessageUtil.sendMessage(player, DMessage.ERROR_REQUIREMENTS.getMessage()); msgs.forEach(msg -> player.spigot().sendMessage(msg)); } return fulfilled || DPermission.hasPermission(player, DPermission.IGNORE_REQUIREMENTS); } public void giveLoot(Dungeon dungeon, List rewards) { if (!canLoot(dungeon)) { return; } rewards.forEach(r -> r.giveTo(player.getPlayer())); if (dungeon != null) { getData().logTimeLastLoot(dungeon.getName()); } } public boolean canLoot(Dungeon dungeon) { return getTimeNextLoot(dungeon) <= getData().getTimeLastStarted(dungeon.getName()); } public long getTimeNextLoot(Dungeon dungeon) { return dungeon.getRules().getState(GameRule.TIME_TO_NEXT_LOOT) * 60 * 60 * 1000 + getData().getTimeLastLoot(dungeon.getName()); } /* Actions */ @Override public void sendMessage(String message) { MessageUtil.sendMessage(player, message); } public void heal() { if (is1_9) { player.setHealth(player.getAttribute(AttributeUtil.MAX_HEALTH).getValue()); } else { player.setHealth(player.getMaxHealth()); } player.setFoodLevel(20); player.setFireTicks(0); for (PotionEffect effect : player.getActivePotionEffects()) { player.removePotionEffect(effect.getType()); } } @Override public void reset(boolean gameFinished) { Location tpLoc; boolean keepInventory; if (getGroup() != null) { Dungeon dungeon = getGroup().getDungeon(); GameRuleContainer rules = dungeon.getRules(); keepInventory = rules.getState(gameFinished ? GameRule.KEEP_INVENTORY_ON_FINISH : GameRule.KEEP_INVENTORY_ON_ESCAPE); LocationString tpLocString = LocationString.fromString(rules.getState(gameFinished ? GameRule.FINISH_LOCATION : GameRule.ESCAPE_LOCATION)); if (tpLocString != null && tpLocString.getLocation() != null) { tpLoc = tpLocString.getLocation(); } else { if (data.getOldLocation().getWorld() != null) { tpLoc = data.getOldLocation(); } else { tpLoc = Bukkit.getWorlds().get(0).getSpawnLocation(); } } } else { if (data.getOldLocation().getWorld() != null) { tpLoc = data.getOldLocation(); } else { tpLoc = Bukkit.getWorlds().get(0).getSpawnLocation(); } keepInventory = false; } if (player.isDead()) { new BukkitRunnable() { @Override public void run() { player.spigot().respawn(); reset(tpLoc, keepInventory); } }.runTaskLater(plugin, 1L); } else { reset(tpLoc, keepInventory); } } @Override public void reset(Location tpLoc, boolean keepInventory) { try { player.teleport(tpLoc); player.setGameMode(data.getOldGameMode()); if (!keepInventory) { while (data.getOldInventory().size() > 36) { data.getOldInventory().remove(36); } player.getInventory().setContents(data.getOldInventory().toArray(new ItemStack[36])); player.getInventory().setArmorContents(data.getOldArmor().toArray(new ItemStack[4])); if (is1_9) { player.getInventory().setItemInOffHand(data.getOldOffHand()); } player.setLevel(data.getOldLevel()); player.setExp(data.getOldExp()); player.setFoodLevel(data.getOldFoodLevel()); player.setFireTicks(data.getOldFireTicks()); for (PotionEffect effect : player.getActivePotionEffects()) { player.removePotionEffect(effect.getType()); } player.addPotionEffects(data.getOldPotionEffects()); if (is1_9) { player.setCollidable(data.getOldCollidabilityState()); player.setInvulnerable(data.getOldInvulnerabilityState()); for (Attribute attribute : Attribute.values()) { AttributeInstance instance = player.getAttribute(attribute); if (instance == null) { continue; } instance.setBaseValue((double) data.getOldAttributeBases().get(attribute)); for (AttributeModifier mod : instance.getModifiers()) { instance.removeModifier(mod); } for (/*AttributeModifier*/Object mod : data.getOldAttributeMods().get(attribute)) { // TODO Remove casts when 1.8 is dropped instance.addModifier((AttributeModifier) mod); } } } player.setAllowFlight(data.getOldFlyingState()); } else { for (ItemStack item : player.getInventory().getContents()) { if (item == null) { continue; } if (plugin.isDungeonItem(item)) { item.setAmount(0); } } } } catch (NullPointerException exception) { exception.printStackTrace(); player.setHealth(0); MessageUtil.log(plugin, "&4Killed player &6" + player.getName() + "&4 because the data to restore his main inventory is corrupted :("); } data.clearPlayerState(); } /** * Starts the tutorial */ public void startTutorial() { Dungeon dungeon = plugin.getMainConfig().getTutorialDungeon(); if (dungeon == null) { MessageUtil.sendMessage(player, DMessage.ERROR_TUTORIAL_DOES_NOT_EXIST.getMessage()); return; } if (xlib.getPermissionProvider() != null && xlib.getPermissionProvider().hasGroupSupport()) { String startGroup = plugin.getMainConfig().getTutorialStartGroup(); if (startGroup == null) { return; } if (xlib.isGroupEnabled(startGroup)) { xlib.getPermissionProvider().playerAddGroup(player, startGroup); } } DGroup dGroup = DGroup.create(plugin, Cause.TUTORIAL, player, null, null, dungeon); if (dGroup == null) { return; } Game game = new DGame(plugin, dungeon, dGroup); game.setTutorial(true); GameWorld gameWorld = game.ensureWorldIsLoaded(true); new DGamePlayer(plugin, player, gameWorld); } @Override public String toString() { return getClass().getSimpleName() + "{player=" + player + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DGroup.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameGoal; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.event.group.GroupCollectRewardEvent; import de.erethon.dungeonsxl.api.event.group.GroupCreateEvent; import de.erethon.dungeonsxl.api.event.group.GroupDisbandEvent; import de.erethon.dungeonsxl.api.event.group.GroupFinishDungeonEvent; import de.erethon.dungeonsxl.api.event.group.GroupFinishFloorEvent; import de.erethon.dungeonsxl.api.event.group.GroupPlayerJoinEvent; import de.erethon.dungeonsxl.api.event.group.GroupStartFloorEvent; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.player.PlayerCache; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.player.PlayerGroup.Color; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DDungeon; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.dungeon.DungeonConfig; import de.erethon.dungeonsxl.global.GroupSign; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.player.PlayerCollection; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; /** * @author Frank Baumann, Daniel Saukel */ public class DGroup implements PlayerGroup { private DungeonsXL plugin; private PlayerCache dPlayers; private static int counter; private int id; private String name; private String untaggedName; private GroupSign groupSign; private Player captain; private PlayerCollection players = new PlayerCollection(); private PlayerCollection invitedPlayers = new PlayerCollection(); private Dungeon dungeon; private Game game; private List rewards = new ArrayList<>(); private BukkitTask timeIsRunningTask; private Color color; private int score = 0; private int initialLives = -1; private int lives = -1; private DGroup() { } public static DGroup create(DungeonsXL plugin, GroupCreateEvent.Cause cause, Player leader, String name, Color color, Dungeon dungeon) { if (name == null) { name = color != null ? color.toString() : "Group"; } DGroup group = new DGroup(); group.plugin = plugin; group.dPlayers = plugin.getPlayerCache(); group.id = counter++; group.untaggedName = name; group.name = name + "#" + group.id; group.color = color; group.dungeon = dungeon; group.addMember(leader); group.setLeader(leader); GroupCreateEvent event = new GroupCreateEvent(group, plugin.getPlayerCache().get(leader), cause); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { plugin.getGroupCache().add(group.name, group); return group; } return null; } // Getters and setters @Override public int getId() { return id; } @Override public String getName() { return getDColor().getChatColor() + name; } @Override public String getRawName() { return name; } @Override public void setName(String name) { plugin.getGroupCache().remove(this); untaggedName = name; this.name = name + "#" + id; plugin.getGroupCache().add(name, this); } public String getUntaggedName() { return untaggedName; } public GroupSign getGroupSign() { return groupSign; } public void setGroupSign(GroupSign groupSign) { this.groupSign = groupSign; } @Override public Player getLeader() { return captain; } @Override public void setLeader(Player player) { captain = player; } @Override public PlayerCollection getMembers() { return players; } /** * @return the players as a Set<DGlobalPlayer> */ public Set getDGlobalPlayers() { Set players = new HashSet<>(); for (UUID uuid : this.players) { players.add((DGlobalPlayer) dPlayers.get(uuid)); } return players; } public Set getDGamePlayers() { Set players = new HashSet<>(); for (UUID uuid : this.players) { GlobalPlayer dPlayer = dPlayers.get(uuid); if (dPlayer instanceof DGamePlayer) { players.add((DGamePlayer) dPlayer); } } return players; } @Override public void addMember(Player player, boolean message) { GroupPlayerJoinEvent event = new GroupPlayerJoinEvent(this, dPlayers.get(player), false); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } if (message) { sendMessage(DMessage.GROUP_PLAYER_JOINED.getMessage(player.getName())); MessageUtil.sendMessage(player, DMessage.PLAYER_JOIN_GROUP.getMessage()); } players.add(player.getUniqueId()); } @Override public void removeMember(Player player, boolean message) { players.remove(player.getUniqueId()); plugin.getGlobalProtectionCache().updateGroupSigns(this); if (message) { sendMessage(DMessage.PLAYER_LEFT_GROUP.getMessage(player.getName())); } if (isEmpty()) { GroupDisbandEvent event = new GroupDisbandEvent(this, dPlayers.get(player), GroupDisbandEvent.Cause.GROUP_IS_EMPTY); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { delete(); } } } @Override public PlayerCollection getInvitedPlayers() { return invitedPlayers; } @Override public void addInvitedPlayer(Player player, boolean silent) { if (player == null) { return; } if (plugin.getPlayerGroup(player) != null) { if (!silent) { MessageUtil.sendMessage(getLeader(), DMessage.ERROR_IN_GROUP.getMessage(player.getName())); } return; } if (!silent) { MessageUtil.sendMessage(player, DMessage.PLAYER_INVITED.getMessage(getLeader().getName(), name)); } // Send message if (!silent) { sendMessage(DMessage.GROUP_INVITED_PLAYER.getMessage(getLeader().getName(), player.getName(), name)); } // Add player invitedPlayers.add(player.getUniqueId()); } @Override public void removeInvitedPlayer(Player player, boolean silent) { if (player == null) { return; } if (plugin.getPlayerGroup(player) != this) { if (!silent) { MessageUtil.sendMessage(getLeader(), DMessage.ERROR_NOT_IN_GROUP.getMessage(player.getName(), name)); } return; } if (!silent) { MessageUtil.sendMessage(player, DMessage.PLAYER_UNINVITED.getMessage(player.getName(), name)); } // Send message if (!silent) { for (Player groupPlayer : players.getOnlinePlayers()) { MessageUtil.sendMessage(groupPlayer, DMessage.GROUP_UNINVITED_PLAYER.getMessage(getLeader().getName(), player.getName(), name)); } } invitedPlayers.remove(player.getUniqueId()); } @Override public void clearOfflineInvitedPlayers() { ArrayList toRemove = new ArrayList<>(); for (UUID uuid : invitedPlayers.getUniqueIds()) { if (Bukkit.getPlayer(uuid) == null) { toRemove.add(uuid); } } invitedPlayers.removeAll(toRemove); } @Override public Game getGame() { return game; } public void setGame(Game game) { this.game = game; } @Override public Dungeon getDungeon() { return game != null ? game.getDungeon() : dungeon; } /** * {@link #getDungeon()} ignores this if the group is in a game. * * @param dungeon dungeon to set */ public void setDungeon(Dungeon dungeon) { this.dungeon = dungeon; } /** * Sets the dungeon. * * @param name the name of the dungeon * @return if the action was successful */ public boolean setDungeon(String name) { return (dungeon = plugin.getDungeonRegistry().get(name)) != null; } public String getDungeonName() { if (getDungeon() == null) { return null; } return getDungeon().getName(); } public String getMapName() { return getGameWorld() == null ? null : getGameWorld().getName(); } @Override public boolean isPlaying() { return game != null; } @Override public List getRewards() { return rewards; } @Override public void addReward(Reward reward) { GroupCollectRewardEvent event = new GroupCollectRewardEvent(this, null, reward); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } rewards.add(reward); } @Override public void removeReward(Reward reward) { rewards.remove(reward); } public BukkitTask getTimeIsRunningTask() { return timeIsRunningTask; } public void setTimeIsRunningTask(BukkitTask task) { this.timeIsRunningTask = task; } public boolean isEmpty() { return players.size() == 0; } public boolean isCustom() { return !name.matches("Group#[0-9]{1,}"); } /** * Returns the color that represents this group. * * @return the color that represents this group */ public Color getDColor() { if (color != null) { return color; } else { return Color.WHITE; } } /** * Sets the color that represents this group. * * @param color the group color to set */ public void setDColor(Color color) { this.color = color; } @Override public int getScore() { return score; } @Override public void setScore(int score) { this.score = score; } @Override public int getInitialLives() { return initialLives; } @Override public void setInitialLives(int initialLives) { this.initialLives = initialLives; } @Override public int getLives() { return lives; } @Override public void setLives(int lives) { this.lives = lives; } @Override public boolean isFinished() { for (DGamePlayer player : getDGamePlayers()) { if (!player.isFinished()) { return false; } } return true; } /* Actions */ public boolean teleport() { if (dungeon == null || dungeon.getMap() == null) { sendMessage(DMessage.ERROR_NO_SUCH_DUNGEON.getMessage()); return false; } if (game == null) { game = new DGame(plugin, dungeon, this); } GameWorld target = game.ensureWorldIsLoaded(false); if (target == null) { sendMessage(DMessage.ERROR_TOO_MANY_INSTANCES.getMessage()); return false; } for (OfflinePlayer offline : players.getOfflinePlayers()) { if (!offline.isOnline()) { players.remove(offline); } Player player = offline.getPlayer(); new DGamePlayer(plugin, player, target); } return true; } /** * The group finishs the dungeon. */ public void finish() { GroupFinishDungeonEvent groupFinishDungeonEvent = new GroupFinishDungeonEvent(this, dungeon); Bukkit.getPluginManager().callEvent(groupFinishDungeonEvent); if (groupFinishDungeonEvent.isCancelled()) { return; } ((DGame) getGame()).resetWaveKills(); getDGamePlayers().forEach(p -> p.leave(false)); } // TODO: Move code to more appropriate classes /** * The group finishs the current floor. * * @param specifiedFloor the name of the next floor */ public void finishFloor(DResourceWorld specifiedFloor) { Game game = getGame(); DungeonConfig dConfig = ((DDungeon) dungeon).getConfig(); int floorsLeft = getDungeon().getFloors().size() - game.getFloorCount(); //floorCount contains start floor, but dungeon floor list doesn't game.removeUnplayedFloor(game.getWorld().getResource(), false); ResourceWorld newFloor = null; GameWorld.Type type = null; if (game.getWorld().getType() == GameWorld.Type.END_FLOOR) { finish(); return; } else if (specifiedFloor != null) { newFloor = specifiedFloor; type = GameWorld.Type.DEFAULT; } else if (floorsLeft > 0) { int random = new Random().nextInt(floorsLeft); newFloor = game.getUnplayedFloors().get(random); type = GameWorld.Type.DEFAULT; } else { newFloor = dConfig.getEndFloor(); type = GameWorld.Type.END_FLOOR; } GroupFinishFloorEvent event = new GroupFinishFloorEvent(this, game.getWorld(), newFloor); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } GameWorld gameWorld = newFloor.instantiateGameWorld(getGame(), true); gameWorld.setType(type); game.setWorld(gameWorld); for (DGamePlayer player : getDGamePlayers()) { player.setInstanceWorld(gameWorld); player.setLastCheckpoint(gameWorld.getStartLocation(this)); if (player.getWolf() != null) { player.getWolf().teleport(player.getLastCheckpoint()); } player.setFinished(false); } game.start(); } @Override public void delete() { Game game = getGame(); plugin.getGroupCache().remove(this); if (game != null) { game.removeGroup(this); } for (UUID uuid : players.getUniqueIds()) { GlobalPlayer member = dPlayers.get(uuid); if (member instanceof InstancePlayer) { ((InstancePlayer) member).leave(); } } if (timeIsRunningTask != null) { timeIsRunningTask.cancel(); } plugin.getGlobalProtectionCache().updateGroupSigns(this); plugin.getGroupAdapters().forEach(a -> a.removeReference(this)); } public boolean checkStartGame(Game game) { for (Player player : getMembers().getOnlinePlayers()) { GamePlayer gamePlayer = plugin.getPlayerCache().getGamePlayer(player); if (gamePlayer == null) { gamePlayer = new DGamePlayer(plugin, player, getGameWorld()); } if (!gamePlayer.isReady()) { return false; } } GroupStartFloorEvent event = new GroupStartFloorEvent(this, getGameWorld()); Bukkit.getPluginManager().callEvent(event); return !event.isCancelled(); } public void startGame(Game game, int index) { if (color == null) { color = plugin.getMainConfig().getGroupColorPriority(index); } plugin.getGlobalProtectionCache().updateGroupSigns(this); GameRuleContainer rules = getDungeon().getRules(); initialLives = rules.getState(GameRule.INITIAL_GROUP_LIVES); lives = initialLives; GameGoal goal = rules.getState(GameRule.GAME_GOAL); if (goal.getType().hasComponent(GameGoal.TIME_TO_FINISH) && goal.getState(GameGoal.TIME_TO_FINISH) != -1) { timeIsRunningTask = new TimeIsRunningTask(plugin, this, goal.getState(GameGoal.TIME_TO_FINISH)).runTaskTimer(plugin, 20, 20); } for (UUID playerId : getMembers()) { GlobalPlayer player = plugin.getPlayerCache().get(playerId); if (!(player instanceof DGamePlayer)) { MessageUtil.debug(plugin, "[ERROR] Player isn't a DGamePlayer, registry: " + plugin.getPlayerCache().getAll()); return; } ((DGamePlayer) player).startGame(); } } public void winGame() { String title = DMessage.GROUP_CONGRATS.getMessage(); String subtitle = DMessage.GROUP_CONGRATS_SUB.getMessage(getName()); for (DGamePlayer player : getDGamePlayers()) { player.leave(false); MessageUtil.sendTitleMessage(player.getPlayer(), title, subtitle, 20, 20, 100); } } // This is not used. public boolean checkRequirements() { if (DPermission.hasPermission(getLeader(), DPermission.IGNORE_REQUIREMENTS)) { return true; } for (DGamePlayer dPlayer : getDGamePlayers()) { if (!dPlayer.checkRequirements(dungeon)) { return false; } } return true; } /** * Sends a message to all players in the group. * * @param message the message to send */ public void sendMessage(String message) { for (Player player : players.getOnlinePlayers()) { if (player.isOnline()) { MessageUtil.sendMessage(player, message); } } } @Override public String toString() { return getClass().getSimpleName() + "{name=" + name + "; captain=" + captain + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DGroupTag.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import com.gmail.filoghost.holographicdisplays.api.Hologram; import com.gmail.filoghost.holographicdisplays.api.HologramsAPI; import de.erethon.dungeonsxl.DungeonsXL; /** * @author Daniel Saukel */ public class DGroupTag { private DGamePlayer player; private Hologram hologram; public DGroupTag(DungeonsXL plugin, DGamePlayer player) { this.player = player; DGroup group = player.getGroup(); if (group != null) { hologram = HologramsAPI.createHologram(plugin, player.getPlayer().getLocation().clone().add(0, 3.5, 0)); hologram.appendItemLine(group.getDColor().getWoolMaterial().toItemStack()); hologram.appendTextLine(group.getName()); } } public void update() { hologram.teleport(player.getPlayer().getLocation().clone().add(0, 3.5, 0)); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DInstancePlayer.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.MainConfig; import de.erethon.dungeonsxl.util.AttributeUtil; import de.erethon.dungeonsxl.util.ParsingUtil; import org.bukkit.World; import org.bukkit.entity.Player; import org.bukkit.potion.PotionEffect; /** * @author Daniel Saukel */ public abstract class DInstancePlayer extends DGlobalPlayer implements InstancePlayer { protected MainConfig config; private InstanceWorld instanceWorld; DInstancePlayer(DungeonsXL plugin, Player player, InstanceWorld world) { super(plugin, player, false); config = plugin.getMainConfig(); instanceWorld = world; getData().savePlayerState(player); } /* Getters and setters */ @Override public InstanceWorld getInstanceWorld() { return instanceWorld; } @Override public World getWorld() { return instanceWorld.getWorld(); } public void setInstanceWorld(InstanceWorld instanceWorld) { this.instanceWorld = instanceWorld; } // Players in dungeons never get announcer messages @Override public boolean isAnnouncerEnabled() { return false; } /* Actions */ /** * Clear the player's inventory, potion effects etc. *

* Does NOT handle flight. */ public void clearPlayerData() { player.getInventory().clear(); player.getInventory().setArmorContents(null); player.setExp(0f); player.setLevel(0); double maxHealth; if (is1_9) { AttributeUtil.resetPlayerAttributes(player); maxHealth = player.getAttribute(AttributeUtil.MAX_HEALTH).getValue(); } else { maxHealth = player.getMaxHealth(); } player.setHealth(maxHealth); player.setFoodLevel(20); player.setExp(0f); player.setLevel(0); for (PotionEffect effect : player.getActivePotionEffects()) { player.removePotionEffect(effect.getType()); } if (is1_9) { player.setCollidable(true); player.setInvulnerable(false); } } /** * Delete this DInstancePlayer. Creates a DGlobalPlayer to replace it! */ public void delete() { if (player.isOnline()) { // Create a new DGlobalPlayer (outside a dungeon) new DGlobalPlayer(this); } else { plugin.getPlayerCache().remove(this); } } /** * Makes the player send a message to the world. * * @param message the message to send */ public void chat(String message) { String chatFormat = instanceWorld instanceof GameWorld ? config.getChatFormatGame() : config.getChatFormatEdit(); instanceWorld.sendMessage(ParsingUtil.replaceChatPlaceholders(chatFormat, this) + message); for (GlobalPlayer player : plugin.getPlayerCache()) { if (player.isInChatSpyMode()) { if (!getWorld().getPlayers().contains(player.getPlayer())) { player.sendMessage(ParsingUtil.replaceChatPlaceholders(config.getChatFormatSpy(), this) + message); } } } } /* Abstracts */ /** * Repeating checks for the player. */ public abstract void update(); } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DPermission.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.permissions.Permission; import org.bukkit.permissions.PermissionDefault; import static org.bukkit.permissions.PermissionDefault.*; /** * @author Daniel Saukel */ public enum DPermission { // Main nodes ANNOUNCE("announce", OP), BED("bed", OP), BREAK("break", OP), BYPASS("bypass", OP), CHAT("chat", TRUE), CHAT_SPY("chatspy", OP), CMD_EDIT("cmdedit", OP), CREATE("create", OP), DELETE("delete", OP), DUNGEON_ITEM("dungeonitem", OP), EDIT("edit", OP), ENDER_CHEST("enderchest", OP), DISPENSER("dispenser", OP), ENTER("enter", OP), ESCAPE("escape", TRUE), GAME("game", TRUE), GROUP("group", OP), GROUP_ADMIN("group.admin", OP, GROUP), HELP("help", TRUE), IGNORE_REQUIREMENTS("ignorerequirements", OP), IMPORT("IMPORT", OP), INVITE("invite", OP), INSECURE("insecure", OP), JOIN("join", TRUE), KICK("kick", OP), LEAVE("leave", TRUE), LIST("list", OP), LIVES("lives", TRUE), MAIN("main", TRUE), MESSAGE("msg", OP), PLAY("play", OP), PORTAL("portal", OP), RELOAD("reload", OP), RENAME("rename", OP), RESOURCE_PACK("resourcepack", OP), REWARDS("rewards", TRUE), SAVE("save", OP), STATUS("status", OP), /** * Allows to open the settings menu. */ SETTINGS("settings", TRUE), /** * Allows to modify dungeon settings unless they have an own node. */ SETTINGS_EDIT("settings.edit", OP), /** * Allows to modify global settings. */ SETTINGS_GLOBAL("settings.global", OP), /** * Allows to modify player settings unless they have an own node. */ SETTINGS_PLAYER("settings.player", TRUE), SIGN("sign", OP), TEST("test", OP), UNINVITE("uninvite", OP), // Kits ADMINISTRATOR("*", OP), HALF_EDITOR("halfeditor", OP, ESCAPE, LIST, MESSAGE, SAVE), FULL_EDITOR("fulleditor", OP, HALF_EDITOR, DELETE, EDIT, PLAY, RENAME, SIGN, TEST), HALF_PLAYER("halfplayer", TRUE, CHAT, ESCAPE, GAME, HELP, JOIN, LEAVE, LIVES, MAIN, SETTINGS, SETTINGS_PLAYER), FULL_PLAYER("fullplayer", OP, HALF_PLAYER, GROUP); public static final String PREFIX = "dxl."; private Permission node; private List children = new ArrayList<>(); DPermission(String node, PermissionDefault isDefault) { this.node = new Permission(PREFIX + node, isDefault); } DPermission(String node, PermissionDefault isDefault, DPermission... children) { this(node, isDefault); this.children = Arrays.asList(children); for (DPermission child : children) { child.node.addParent(node, true); } } /** * @return the permission node String */ public String getNode() { return node.getName(); } /** * @return if a player has the node by default */ public PermissionDefault isDefault() { return node.getDefault(); } /** * @return if the node has children */ public boolean hasChildren() { return !children.isEmpty(); } /** * @return the child permissions */ public List getChildren() { return children; } /** * @param node the node String, with or without "dxl." * @return the DPermission value */ public static DPermission getByNode(String node) { for (DPermission permission : values()) { if (permission.getNode().equals(node) || permission.node.equals(node)) { return permission; } } return null; } /** * @param sender the CommandSender * @param permission the permission to check * @return if the player has the permission */ public static boolean hasPermission(CommandSender sender, DPermission permission) { return sender.hasPermission(permission.getNode()); } /** * Registers the permissions. */ public static void register() { for (DPermission permission : values()) { Bukkit.getPluginManager().addPermission(permission.node); } } /** * Unregisters the permissions. */ public static void unregister() { for (DPermission permission : values()) { Bukkit.getPluginManager().removePermission(permission.node); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DPlayerData.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.util.AttributeUtil; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.config.ConfigUtil; import de.erethon.xlib.config.DREConfig; import de.erethon.xlib.util.EnumUtil; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeModifier; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; import org.bukkit.potion.PotionEffect; /** * Represents a player's persistent data. * * @author Daniel Saukel */ public class DPlayerData extends DREConfig { protected boolean is1_9 = Version.isAtLeast(Version.MC1_9); public static final int CONFIG_VERSION = 4; public static final String PREFIX_STATE_PERSISTENCE = "savePlayer."; public static final String PREFIX_STATS = "stats."; // State persistence private boolean keepInventoryAfterLogout = true; private Location oldLocation; private List oldInventory; private List oldArmor; private ItemStack oldOffHand; private int oldLvl; private float oldExp; private double oldHealth; private int oldFoodLevel; private int oldFireTicks; private GameMode oldGameMode; private Collection oldPotionEffects; private Map/**/ oldAttributeBases; private Multimap/**/ oldAttributeMods; private boolean oldCollidabilityState; private boolean oldFlyingState; private boolean oldInvulnerabilityState; // Stats private Map timeLastStarted = new HashMap<>(); private Map timeLastFinished = new HashMap<>(); private Map timeLastLoot = new HashMap<>(); private boolean finishedTutorial; public DPlayerData(File file) { super(file, CONFIG_VERSION); if (initialize) { initialize(); } load(); } /* Getters and setters */ /** * @return if the player was in a game when he left the game */ public boolean wasInGame() { return config.contains(PREFIX_STATE_PERSISTENCE); } /** * @return if the inventory shall be reset after a logout */ public boolean getKeepInventoryAfterLogout() { return keepInventoryAfterLogout; } /** * @param keepInventoryAfterLogout set if the inventory shall be reset after a logout */ public void setKeepInventoryAfterLogout(boolean keepInventoryAfterLogout) { this.keepInventoryAfterLogout = keepInventoryAfterLogout; config.set(PREFIX_STATE_PERSISTENCE + "keepInventoryAfterLogout", keepInventoryAfterLogout); super.save(); } /** * @return the old location */ public Location getOldLocation() { if (oldLocation.getWorld() == null) { return Bukkit.getWorlds().get(0).getSpawnLocation(); } return oldLocation; } /** * @param location the location to set */ public void setOldLocation(Location location) { oldLocation = location; } /** * @return the items in the old inventory */ public List getOldInventory() { return oldInventory; } /** * @param inventory the inventory to set */ public void setOldInventory(List inventory) { oldInventory = inventory; } /** * @return the items in the old armor slots */ public List getOldArmor() { return oldArmor; } /** * @param inventory the inventory to set */ public void setOldArmor(List inventory) { oldArmor = inventory; } /** * @return the items in the old off-hand slot */ public ItemStack getOldOffHand() { return oldOffHand; } /** * @param offHand the off hand item to set */ public void setOldOffHand(ItemStack offHand) { oldOffHand = offHand; } /** * @return the old level */ public int getOldLevel() { return oldLvl; } /** * @param level the level to set */ public void setOldLevel(int level) { oldLvl = level; } /** * @return the old exp */ public float getOldExp() { return oldExp; } /** * @param exp the amount of exp to set */ public void setOldExp(float exp) { oldExp = exp; } /** * @return the old health */ public double getOldHealth() { return oldHealth; } /** * @param health the health to set */ public void setOldHealth(double health) { oldHealth = health; } /** * @return the old food level */ public int getOldFoodLevel() { return oldFoodLevel; } /** * @param foodLevel the food level to set */ public void setOldFoodLevel(int foodLevel) { oldFoodLevel = foodLevel; } /** * @return the old fire ticks */ public int getOldFireTicks() { return oldFireTicks; } /** * @param fireTicks the fire ticks to set */ public void setFireTicks(int fireTicks) { oldFireTicks = fireTicks; } /** * @return the old GameMode */ public GameMode getOldGameMode() { return oldGameMode; } /** * @param gameMode the GameMode to set */ public void setOldGameMode(GameMode gameMode) { oldGameMode = gameMode; } /** * @return the old potion effects */ public Collection getOldPotionEffects() { return oldPotionEffects; } /** * @param potionEffects the potion effects to set */ public void setOldPotionEffects(Collection potionEffects) { oldPotionEffects = potionEffects; } /** * @return the old attribute bases */ public Map/**/ getOldAttributeBases() { return oldAttributeBases; } /** * @param attributeBases the old attribute bases to set */ public void setOldAttributeBases(Map/**/ attributeBases) { oldAttributeBases = attributeBases; } /** * @return the old attribute modifiers */ public Multimap/**/ getOldAttributeMods() { return oldAttributeMods; } /** * @param attributeMods the old attribute modifiers to set */ public void setOldAttributeMods(Multimap/**/ attributeMods) { oldAttributeMods = attributeMods; } /** * @return if the player was collidable */ public boolean getOldCollidabilityState() { return oldCollidabilityState; } /** * @param collidableState the collidable state to set */ public void setOldCollidabilityState(boolean collidableState) { oldCollidabilityState = collidableState; } /** * @return if the player was flying */ public boolean getOldFlyingState() { return oldFlyingState; } /** * @param flyingState the flying state to set */ public void setOldFlyingState(boolean flyingState) { oldFlyingState = flyingState; } /** * @return if the player was invulnerable */ public boolean getOldInvulnerabilityState() { return oldInvulnerabilityState; } /** * @param invulnerabilityState the invulnerability state to set */ public void setOldInvulnerabilityState(boolean invulnerabilityState) { oldFlyingState = invulnerabilityState; } /** * @return a map of the player's started dungeons with dates. */ public Map getTimeLastStarted() { return timeLastStarted; } /** * @param dungeon the dungeon to check * @return the time when the player started the dungeon for the last time */ public long getTimeLastStarted(String dungeon) { Long time = timeLastStarted.get(dungeon.toLowerCase()); if (time == null) { return -1; } else { return time; } } /** * @param dungeon the started dungeon * @param time the time when the dungeon was started */ public void setTimeLastStarted(String dungeon, long time) { timeLastStarted.put(dungeon.toLowerCase(), time); save(); } /** * @return a map of the player's finished dungeons with dates. */ public Map getTimeLastFinished() { return timeLastFinished; } /** * @param dungeon the dungeon to check * @return the time when the player finished the dungeon for the last time */ public long getTimeLastFinished(String dungeon) { Long time = timeLastFinished.get(dungeon.toLowerCase()); if (time == null) { return -1; } else { return time; } } /** * @param dungeon the finished dungeon * @param time the time when the dungeon was finished */ public void setTimeLastFinished(String dungeon, long time) { timeLastFinished.put(dungeon.toLowerCase(), time); save(); } /** * @param dungeon the dungeon to check * @return the time when the player received loot from the dungeon for the last time */ public long getTimeLastLoot(String dungeon) { Long time = timeLastLoot.get(dungeon.toLowerCase()); if (time == null) { return -1; } else { return time; } } /** * @param dungeon the finished dungeon * @param time the time when the dungeon was received */ public void setTimeLastLoot(String dungeon, long time) { timeLastLoot.put(dungeon.toLowerCase(), time); save(); } /** * @return if the player has finished the tutorial */ public boolean hasFinishedTutorial() { return finishedTutorial; } /** * @param finishedTutorial if the player has finished the tutorial */ public void setFinishedTutorial(boolean finishedTutorial) { this.finishedTutorial = finishedTutorial; save(); } /* Actions */ /** * @param dungeon the started dungeon */ public void logTimeLastStarted(String dungeon) { timeLastStarted.put(dungeon.toLowerCase(), System.currentTimeMillis()); save(); } /** * @param dungeon the finished dungeon */ public void logTimeLastFinished(String dungeon) { timeLastFinished.put(dungeon.toLowerCase(), System.currentTimeMillis()); save(); } /** * @param dungeon the finished dungeon */ public void logTimeLastLoot(String dungeon) { timeLastLoot.put(dungeon.toLowerCase(), System.currentTimeMillis()); save(); } @Override public void initialize() { if (!config.contains(PREFIX_STATS + "timeLastStarted")) { config.createSection(PREFIX_STATS + "timeLastStarted"); } if (!config.contains(PREFIX_STATS + "timeLastFinished")) { config.createSection(PREFIX_STATS + "timeLastFinished"); } if (!config.contains(PREFIX_STATS + "timeLastLoot")) { config.createSection(PREFIX_STATS + "timeLastLoot"); } if (!config.contains(PREFIX_STATS + "finishedTutorial")) { config.set(PREFIX_STATS + "finishedTutorial", finishedTutorial); } if (!file.exists()) { try { file.createNewFile(); MessageUtil.log(DungeonsXL.getInstance(), "&6A new player data file has been created and saved as " + file.getName()); } catch (IOException exception) { } } save(); } @Override public void load() { if (config.isConfigurationSection(PREFIX_STATS + "timeLastStarted")) { for (String key : config.getConfigurationSection(PREFIX_STATS + "timeLastStarted").getKeys(false)) { timeLastStarted.put(key, config.getLong(PREFIX_STATS + "timeLastStarted." + key)); } } if (config.isConfigurationSection(PREFIX_STATS + "timeLastFinished")) { for (String key : config.getConfigurationSection(PREFIX_STATS + "timeLastFinished").getKeys(false)) { timeLastFinished.put(key, config.getLong(PREFIX_STATS + "timeLastFinished." + key)); } } if (config.isConfigurationSection(PREFIX_STATS + "timeLastLoot")) { for (String key : config.getConfigurationSection(PREFIX_STATS + "timeLastLoot").getKeys(false)) { timeLastLoot.put(key, config.getLong(PREFIX_STATS + "timeLastLoot." + key)); } } finishedTutorial = config.getBoolean(PREFIX_STATS + "finishedTutorial", finishedTutorial); if (!wasInGame()) { return; } keepInventoryAfterLogout = config.getBoolean(PREFIX_STATE_PERSISTENCE + "keepInventoryAfterLogout"); oldInventory = (List) config.get(PREFIX_STATE_PERSISTENCE + "oldInventory"); oldArmor = (List) config.get(PREFIX_STATE_PERSISTENCE + "oldArmor"); oldOffHand = (ItemStack) config.get(PREFIX_STATE_PERSISTENCE + "oldOffHand"); oldLvl = config.getInt(PREFIX_STATE_PERSISTENCE + "oldLvl"); oldExp = config.getInt(PREFIX_STATE_PERSISTENCE + "oldExp"); oldHealth = config.getDouble(PREFIX_STATE_PERSISTENCE + "oldHealth"); oldFoodLevel = config.getInt(PREFIX_STATE_PERSISTENCE + "oldFoodLevel"); oldFireTicks = config.getInt(PREFIX_STATE_PERSISTENCE + "oldFireTicks"); if (EnumUtil.isValidEnum(GameMode.class, config.getString(PREFIX_STATE_PERSISTENCE + "oldGameMode"))) { oldGameMode = GameMode.valueOf(config.getString(PREFIX_STATE_PERSISTENCE + "oldGameMode")); } else { oldGameMode = GameMode.SURVIVAL; } oldPotionEffects = (Collection) config.get(PREFIX_STATE_PERSISTENCE + "oldPotionEffects"); if (is1_9) { Map basesMap = ConfigUtil.getMap(config, PREFIX_STATE_PERSISTENCE + "oldAttributeBases", false); if (basesMap != null) { oldAttributeBases = new HashMap<>(); for (Entry entry : basesMap.entrySet()) { if (!(entry.getValue() instanceof Double)) { continue; } Attribute attribute = AttributeUtil.get(entry.getKey()); Double base = (Double) entry.getValue(); oldAttributeBases.put(attribute, base); } } Map modsMap = ConfigUtil.getMap(config, PREFIX_STATE_PERSISTENCE + "oldAttributeMods", false); if (modsMap != null) { oldAttributeMods = HashMultimap.create(); for (Entry entry : modsMap.entrySet()) { if (!(entry.getValue() instanceof Collection)) { continue; } Attribute attribute = AttributeUtil.get(entry.getKey()); Collection mods = (Collection) entry.getValue(); oldAttributeMods.putAll(attribute, mods); } } } try { oldLocation = (Location) config.get(PREFIX_STATE_PERSISTENCE + "oldLocation"); } catch (IllegalArgumentException exception) { oldLocation = Bukkit.getWorlds().get(0).getSpawnLocation(); } oldCollidabilityState = config.getBoolean(PREFIX_STATE_PERSISTENCE + "oldCollidabilityState", true); oldFlyingState = config.getBoolean(PREFIX_STATE_PERSISTENCE + "oldFlyingState", false); oldInvulnerabilityState = config.getBoolean(PREFIX_STATE_PERSISTENCE + "oldInvulnerabilityState", false); } @Override public void save() { config.set(PREFIX_STATS + "timeLastStarted", timeLastStarted); config.set(PREFIX_STATS + "timeLastFinished", timeLastFinished); config.set(PREFIX_STATS + "timeLastLoot", timeLastLoot); config.set(PREFIX_STATS + "finishedTutorial", finishedTutorial); super.save(); } /** * Saves the player's data to the file. * * @param player the Player to save */ public void savePlayerState(Player player) { oldGameMode = player.getGameMode(); oldFireTicks = player.getFireTicks(); oldFoodLevel = player.getFoodLevel(); oldHealth = player.getHealth(); oldExp = player.getExp(); oldLvl = player.getLevel(); PlayerInventory inv = player.getInventory(); oldArmor = new ArrayList<>(4); for (int i = 0; i < 4; i++) { ItemStack itemStack = inv.getArmorContents()[i]; oldArmor.add(itemStack != null ? itemStack.clone() : null); } oldInventory = new ArrayList<>(36); for (int i = 0; i < 36; i++) { ItemStack itemStack = inv.getContents()[i]; oldInventory.add(itemStack != null ? itemStack.clone() : null); } if (is1_9) { oldOffHand = inv.getItemInOffHand().clone(); } oldLocation = player.getLocation(); oldPotionEffects = player.getActivePotionEffects(); if (is1_9) { oldAttributeBases = new HashMap<>(); oldAttributeMods = HashMultimap.create(); for (Attribute attribute : Attribute.values()) { AttributeInstance instance = player.getAttribute(attribute); if (instance == null) { continue; } oldAttributeBases.put(attribute, instance.getBaseValue()); for (AttributeModifier mod : instance.getModifiers()) { oldAttributeMods.put(attribute, mod); } } oldCollidabilityState = player.isCollidable(); oldInvulnerabilityState = player.isInvulnerable(); } oldFlyingState = player.getAllowFlight(); config.set(PREFIX_STATE_PERSISTENCE + "oldGameMode", oldGameMode.toString()); config.set(PREFIX_STATE_PERSISTENCE + "oldFireTicks", oldFireTicks); config.set(PREFIX_STATE_PERSISTENCE + "oldFoodLevel", oldFoodLevel); config.set(PREFIX_STATE_PERSISTENCE + "oldHealth", oldHealth); config.set(PREFIX_STATE_PERSISTENCE + "oldExp", oldExp); config.set(PREFIX_STATE_PERSISTENCE + "oldLvl", oldLvl); config.set(PREFIX_STATE_PERSISTENCE + "oldArmor", oldArmor); config.set(PREFIX_STATE_PERSISTENCE + "oldInventory", oldInventory); config.set(PREFIX_STATE_PERSISTENCE + "oldOffHand", oldOffHand); config.set(PREFIX_STATE_PERSISTENCE + "oldLocation", oldLocation); config.set(PREFIX_STATE_PERSISTENCE + "oldPotionEffects", oldPotionEffects); if (is1_9) { for (Object object : oldAttributeBases.entrySet()) { Entry entry = (Entry) object; config.set(PREFIX_STATE_PERSISTENCE + "oldAttributeBases." + entry.getKey().name(), entry.getValue()); } for (Object object : oldAttributeMods.asMap().entrySet()) { Entry> entry = (Entry>) object; config.set(PREFIX_STATE_PERSISTENCE + "oldAttributeMods." + entry.getKey().name(), entry.getValue()); } } config.set(PREFIX_STATE_PERSISTENCE + "oldCollidabilityState", oldCollidabilityState); config.set(PREFIX_STATE_PERSISTENCE + "oldFlyingState", oldFlyingState); config.set(PREFIX_STATE_PERSISTENCE + "oldInvulnerabilityState", oldInvulnerabilityState); save(); } /** * Removes the state data from the file */ public void clearPlayerState() { oldGameMode = null; oldFireTicks = 0; oldFoodLevel = 0; oldHealth = 0; oldExp = 0; oldLvl = 0; oldArmor = null; oldInventory = null; oldOffHand = null; oldLocation = null; oldPotionEffects = null; oldAttributeBases = null; oldAttributeMods = null; oldCollidabilityState = true; oldFlyingState = false; oldInvulnerabilityState = false; if (wasInGame()) { config.set("savePlayer", null); } save(); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/DPlayerListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.player.EditPlayer; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.util.ParsingUtil; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.LockedDoor; import de.erethon.xlib.category.Category; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import java.util.ArrayList; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.FoodLevelChangeEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.AsyncPlayerChatEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerRespawnEvent; import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.projectiles.ProjectileSource; /** * @author Daniel Saukel, Frank Baumann, Milan Albrecht */ public class DPlayerListener implements Listener { private DungeonsXL plugin; public static final String ALL = "@all "; public DPlayerListener(DungeonsXL plugin) { this.plugin = plugin; } @EventHandler public void onEntityDamage(EntityDamageEvent event) { if (!(event.getEntity() instanceof LivingEntity)) { return; } LivingEntity entity = ((LivingEntity) event.getEntity()); if (isCitizensNPC(entity)) { return; } World world = event.getEntity().getWorld(); GameWorld gameWorld = plugin.getGameWorld(world); if (gameWorld == null) { return; } // Deny all Damage in Lobby if (!gameWorld.isPlaying()) { event.setCancelled(true); } boolean dead = entity.getHealth() - event.getFinalDamage() <= 0; if (!dead) { return; } if (entity instanceof Player && !gameWorld.getDungeon().getRules().getState(GameRule.DEATH_SCREEN)) { event.setDamage(0); GamePlayer gamePlayer = plugin.getPlayerCache().getGamePlayer((Player) entity); if (gamePlayer == null) { return; } ((DGamePlayer) gamePlayer).onDeath(null); } if (plugin.getDungeonMob(entity) != null) { String killer = null; if (event instanceof EntityDamageByEntityEvent) { Entity damager = ((EntityDamageByEntityEvent) event).getDamager(); if (damager instanceof Projectile) { if (((Projectile) damager).getShooter() instanceof Player) { damager = (Player) ((Projectile) damager).getShooter(); } } if (damager instanceof Player) { killer = damager.getName(); } } ((DGame) gameWorld.getGame()).addKill(killer); } } @EventHandler public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { World world = event.getEntity().getWorld(); GameWorld gameWorld = plugin.getGameWorld(world); if (gameWorld == null) { return; } Game game = gameWorld.getGame(); if (game == null) { return; } if (!game.hasStarted()) { return; } boolean pvp = game.getRules().getState(GameRule.PLAYER_VERSUS_PLAYER); boolean friendlyFire = game.getRules().getState(GameRule.FRIENDLY_FIRE); Entity attackerEntity = event.getDamager(); Entity attackedEntity = event.getEntity(); if (attackerEntity instanceof Projectile) { ProjectileSource source = ((Projectile) attackerEntity).getShooter(); if (source instanceof Entity) { attackerEntity = (Entity) source; } } Player attackerPlayer = null; Player attackedPlayer = null; PlayerGroup attackerGroup = null; PlayerGroup attackedGroup = null; if (!(attackerEntity instanceof LivingEntity) || !(attackedEntity instanceof LivingEntity)) { return; } if (attackerEntity instanceof Player && attackedEntity instanceof Player) { attackerPlayer = (Player) attackerEntity; attackedPlayer = (Player) attackedEntity; if (attackedPlayer.hasMetadata("NPC") || attackerPlayer.hasMetadata("NPC")) { return; } attackerGroup = plugin.getPlayerGroup(attackerPlayer); attackedGroup = plugin.getPlayerGroup(attackedPlayer); if (!pvp) { event.setCancelled(true); return; } if (attackerGroup != null && attackedGroup != null) { if (!friendlyFire && attackerGroup.equals(attackedGroup)) { event.setCancelled(true); return; } } } // Check Dogs if (attackerEntity instanceof Player || attackedEntity instanceof Player) { for (GamePlayer player : plugin.getPlayerCache().getAllGamePlayersIf(p -> p.getGameWorld() == gameWorld)) { if (player.getWolf() != null) { if (attackerEntity == player.getWolf() || attackedEntity == player.getWolf()) { event.setCancelled(true); return; } } } } for (GamePlayer dPlayer : plugin.getPlayerCache().getAllGamePlayersIf(p -> p.getGameWorld() == gameWorld)) { if (dPlayer.getWolf() != null) { if (attackerEntity instanceof Player || attackedEntity instanceof Player) { if (attackerEntity == dPlayer.getWolf() || attackedEntity == dPlayer.getWolf()) { event.setCancelled(true); return; } } else if (attackerEntity == dPlayer.getWolf() || attackedEntity == dPlayer.getWolf()) { event.setCancelled(false); return; } } } } // Players don't need to eat in lobbies @EventHandler public void onFoodLevelChange(FoodLevelChangeEvent event) { GameWorld gameWorld = plugin.getGameWorld(event.getEntity().getWorld()); if (gameWorld != null) { if (!gameWorld.isPlaying() || !gameWorld.getDungeon().getRules().getState(GameRule.FOOD_LEVEL)) { event.setCancelled(true); } } } @EventHandler(priority = EventPriority.HIGH) public void onPlayerChat(AsyncPlayerChatEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } GlobalPlayer dPlayer = plugin.getPlayerCache().get(player); if (dPlayer == null) { return; } if (!dPlayer.isInGroupChat()) { return; } if (dPlayer instanceof DEditPlayer) { event.setCancelled(true); ((DInstancePlayer) dPlayer).chat(event.getMessage()); return; } PlayerGroup group = plugin.getPlayerGroup(player); if (group == null) { return; } boolean game = event.getMessage().startsWith(ALL) && dPlayer instanceof DInstancePlayer; event.setCancelled(true); if (game) { ((DInstancePlayer) dPlayer).chat(event.getMessage().substring(ALL.length())); } else { group.sendMessage(ParsingUtil.replaceChatPlaceholders(plugin.getMainConfig().getChatFormatGroup(), dPlayer) + event.getMessage()); } } @EventHandler public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } if (DPermission.hasPermission(player, DPermission.BYPASS)) { return; } if (!(plugin.getPlayerCache().get(player) instanceof DInstancePlayer)) { return; } InstancePlayer dPlayer = plugin.getPlayerCache().getInstancePlayer(player); String command = event.getMessage().toLowerCase(); ArrayList commandWhitelist = new ArrayList<>(); if (dPlayer instanceof DEditPlayer) { if (DPermission.hasPermission(player, DPermission.CMD_EDIT)) { return; } else { commandWhitelist.addAll(plugin.getMainConfig().getEditCommandWhitelist()); } } else { commandWhitelist.addAll(dPlayer.getGroup().getDungeon().getRules().getState(GameRule.GAME_COMMAND_WHITELIST)); } commandWhitelist.add("dungeonsxl"); commandWhitelist.add("dungeon"); commandWhitelist.add("dxl"); event.setCancelled(true); for (String whitelistEntry : commandWhitelist) { if (command.equals('/' + whitelistEntry.toLowerCase()) || command.startsWith('/' + whitelistEntry.toLowerCase() + ' ')) { event.setCancelled(false); } } if (event.isCancelled()) { MessageUtil.sendMessage(player, DMessage.ERROR_CMD.getMessage()); } } @EventHandler public void onPlayerDeath(PlayerDeathEvent event) { Player player = event.getEntity(); if (isCitizensNPC(player)) { return; } DGamePlayer dPlayer = (DGamePlayer) plugin.getPlayerCache().getGamePlayer(player); if (dPlayer == null) { return; } dPlayer.onDeath(event); } @EventHandler(priority = EventPriority.HIGH) public void onPlayerDropItem(PlayerDropItemEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } GlobalPlayer dPlayer = plugin.getPlayerCache().get(player); if (dPlayer == null) { return; } if (dPlayer instanceof EditPlayer && !plugin.getMainConfig().getDropItems() && !DPermission.hasPermission(player, DPermission.INSECURE)) { event.setCancelled(true); } if (!(dPlayer instanceof DGamePlayer)) { return; } DGamePlayer gamePlayer = (DGamePlayer) dPlayer; DGroup dGroup = gamePlayer.getGroup(); if (dGroup == null) { return; } if (!dGroup.isPlaying()) { event.setCancelled(true); return; } if (!gamePlayer.isReady()) { event.setCancelled(true); return; } for (ExItem item : dGroup.getDungeon().getRules().getState(GameRule.SECURE_OBJECTS)) { if (event.getItemDrop().getItemStack().isSimilar(item.toItemStack())) { event.setCancelled(true); MessageUtil.sendMessage(player, DMessage.ERROR_DROP.getMessage()); return; } } } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); if (plugin.checkPlayer(player)) { return; } DGlobalPlayer dPlayer = new DGlobalPlayer(plugin, player); if (dPlayer.getData().wasInGame()) { dPlayer.reset(dPlayer.getData().getOldLocation() != null ? dPlayer.getData().getOldLocation() : Bukkit.getWorlds().get(0).getSpawnLocation(), dPlayer.getData().getKeepInventoryAfterLogout()); } if (!dPlayer.getData().hasFinishedTutorial() && plugin.getMainConfig().isTutorialActivated()) { if (plugin.getInstanceCache().size() < plugin.getMainConfig().getMaxInstances()) { dPlayer.startTutorial(); } else { event.getPlayer().kickPlayer(DMessage.ERROR_TOO_MANY_TUTORIALS.getMessage()); } } } @EventHandler public void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } DGameWorld gameWorld = (DGameWorld) plugin.getGameWorld(player.getWorld()); DGamePlayer gamePlayer = (DGamePlayer) plugin.getPlayerCache().getGamePlayer(player); if (gameWorld != null && gamePlayer != null) { if (gamePlayer.getDGroupTag() != null) { gamePlayer.getDGroupTag().update(); } if (gamePlayer.isStealingFlag()) { DGroup group = gamePlayer.getGroup(); Location startLocation = gameWorld.getStartLocation(group); if (startLocation.distance(player.getLocation()) < 3) { gamePlayer.captureFlag(); } } } } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); GlobalPlayer dPlayer = plugin.getPlayerCache().get(player); PlayerGroup dGroup = dPlayer.getGroup(); if (!(dPlayer instanceof InstancePlayer)) { if (dGroup != null) { dGroup.removeMember(player); } } else if (dPlayer instanceof GamePlayer) { int timeUntilKickOfflinePlayer = dGroup.getDungeon().getRules().getState(GameRule.TIME_UNTIL_KICK_OFFLINE_PLAYER); if (timeUntilKickOfflinePlayer == 0) { ((InstancePlayer) dPlayer).leave(); } else if (timeUntilKickOfflinePlayer > 0) { dGroup.sendMessage(DMessage.PLAYER_OFFLINE.getMessage(dPlayer.getName(), String.valueOf(timeUntilKickOfflinePlayer)), player); ((GamePlayer) dPlayer).setOfflineTimeMillis(System.currentTimeMillis() + timeUntilKickOfflinePlayer * 1000); return; } else { dGroup.sendMessage(DMessage.PLAYER_OFFLINE_NEVER.getMessage(dPlayer.getName()), player); return; } } else if (dPlayer instanceof InstancePlayer) { ((InstancePlayer) dPlayer).leave(); } plugin.getPlayerCache().remove(plugin.getPlayerCache().get(player)); } @EventHandler public void onPlayerRespawn(PlayerRespawnEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } InstancePlayer instancePlayer = plugin.getPlayerCache().getInstancePlayer(player); if (instancePlayer == null) { return; } InstanceWorld instance = instancePlayer.getInstanceWorld(); if (instance == null) { return; } GamePlayer gamePlayer = null; PlayerGroup group = instancePlayer.getGroup(); Location respawn = null; boolean shouldResetInventory = false; if (instancePlayer instanceof GamePlayer) { gamePlayer = ((GamePlayer) instancePlayer); respawn = gamePlayer.getLastCheckpoint(); if (respawn == null) { respawn = group.getGameWorld().getStartLocation(group); } shouldResetInventory = gamePlayer.getGameWorld().getDungeon().getRules().getState(GameRule.RESET_CLASS_INVENTORY_ON_RESPAWN); // Don't forget Doge! if (gamePlayer.getWolf() != null) { gamePlayer.getWolf().teleport(respawn); } } if (respawn == null) { respawn = instancePlayer.getInstanceWorld().getLobbyLocation(); } if (respawn == null) { respawn = instancePlayer.getInstanceWorld().getWorld().getSpawnLocation(); } // Because some plugins set another respawn point, DXL teleports a few ticks later. event.setRespawnLocation(respawn); new RespawnTask(player, gamePlayer, respawn, shouldResetInventory).runTaskLater(plugin, 10L); } @EventHandler(priority = EventPriority.HIGHEST) public void onPlayerTeleport(PlayerTeleportEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } GlobalPlayer dPlayer = plugin.getPlayerCache().get(player); World toWorld = event.getTo().getWorld(); if (dPlayer instanceof InstancePlayer && ((InstancePlayer) dPlayer).getWorld() == toWorld) { return; } if (plugin.getInstanceWorld(toWorld) != null) { dPlayer.sendMessage(DMessage.ERROR_JOIN_GROUP.getMessage()); dPlayer.sendMessage(ChatColor.GOLD + DMessage.CMD_ENTER_HELP.getMessage()); event.setCancelled(true); } } public static boolean isCitizensNPC(LivingEntity entity) { return entity.hasMetadata("NPC"); } /* SUBJECT TO CHANGE */ @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); if (isCitizensNPC(player)) { return; } Block clickedBlock = event.getClickedBlock(); DGameWorld gameWorld = (DGameWorld) plugin.getGameWorld(player.getWorld()); if (clickedBlock != null) { // Block Enderchests if (gameWorld != null || plugin.getEditWorld(player.getWorld()) != null) { if (event.getAction() != Action.LEFT_CLICK_BLOCK) { if (VanillaItem.ENDER_CHEST.is(clickedBlock)) { if (!DPermission.hasPermission(player, DPermission.BYPASS) && !DPermission.hasPermission(player, DPermission.ENDER_CHEST)) { MessageUtil.sendMessage(player, DMessage.ERROR_ENDERCHEST.getMessage()); event.setCancelled(true); } } else if (Category.BEDS.containsBlock(clickedBlock)) { if (!DPermission.hasPermission(player, DPermission.BYPASS) && !DPermission.hasPermission(player, DPermission.BED)) { MessageUtil.sendMessage(player, DMessage.ERROR_BED.getMessage()); event.setCancelled(true); } } } } // Block Dispensers if (gameWorld != null) { if (event.getAction() != Action.LEFT_CLICK_BLOCK) { if (VanillaItem.DISPENSER.is(clickedBlock)) { if (!DPermission.hasPermission(player, DPermission.BYPASS) && !DPermission.hasPermission(player, DPermission.DISPENSER)) { MessageUtil.sendMessage(player, DMessage.ERROR_DISPENSER.getMessage()); event.setCancelled(true); } } } for (LockedDoor door : gameWorld.getLockedDoors()) { if (clickedBlock.equals(door.getBlock()) || clickedBlock.equals(door.getAttachedBlock())) { event.setCancelled(true); return; } } } } // Check Portals if (event.getItem() != null) { ItemStack item = event.getItem(); // Copy/Paste a Sign and Block-info if (plugin.getEditWorld(player.getWorld()) != null) { if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { if (VanillaItem.STICK.is(item)) { DEditPlayer editPlayer = (DEditPlayer) plugin.getPlayerCache().getEditPlayer(player); if (editPlayer != null) { editPlayer.poke(clickedBlock); event.setCancelled(true); } } } } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/RespawnTask.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.api.player.GamePlayer; import org.bukkit.Location; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Daniel Saukel */ public class RespawnTask extends BukkitRunnable { private Player player; private GamePlayer dPlayer; private Location location; private boolean resetClassInventory; public RespawnTask(Player player, GamePlayer dPlayer, Location location, boolean resetClassInventory) { this.location = location; this.player = player; this.dPlayer = dPlayer; this.resetClassInventory = resetClassInventory; } @Override public void run() { if (!player.isOnline()) { return; } if (player.getWorld() != location.getWorld() || player.getLocation().distance(location) > 2) { player.teleport(location); } if (resetClassInventory) { dPlayer.setPlayerClass(dPlayer.getPlayerClass()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/SecureModeTask.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class SecureModeTask extends BukkitRunnable { private DungeonsXL plugin; public SecureModeTask(DungeonsXL plugin) { this.plugin = plugin; } @Override public void run() { for (Player player : Bukkit.getOnlinePlayers()) { GlobalPlayer globalPlayer = plugin.getPlayerCache().get(player); if (globalPlayer == null) { globalPlayer = new DGlobalPlayer(plugin, player); } if (!(globalPlayer instanceof InstancePlayer)) { if (player.getWorld().getName().startsWith("DXL_Game_") | player.getWorld().getName().startsWith("DXL_Edit_") && !DPermission.hasPermission(player, DPermission.INSECURE)) { player.teleport(Bukkit.getWorlds().get(0).getSpawnLocation()); } } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/TimeIsRunningTask.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.group.GroupPlayerKickEvent; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class TimeIsRunningTask extends BukkitRunnable { private DungeonsXL plugin; private PlayerGroup group; private int time; private int timeLeft; public TimeIsRunningTask(DungeonsXL plugin, PlayerGroup group, int time) { this.plugin = plugin; this.group = group; this.time = time; this.timeLeft = time; } @Override public void run() { timeLeft--; String color = ChatColor.GREEN.toString(); try { color = (double) timeLeft / (double) time > 0.25 ? ChatColor.GREEN.toString() : ChatColor.DARK_RED.toString(); } catch (ArithmeticException exception) { color = ChatColor.DARK_RED.toString(); } finally { for (Player player : group.getMembers().getOnlinePlayers()) { MessageUtil.sendActionBarMessage(player, DMessage.PLAYER_TIME_LEFT.getMessage(color, String.valueOf(timeLeft))); GamePlayer dPlayer = plugin.getPlayerCache().getGamePlayer(player); if (timeLeft > 0) { continue; } GroupPlayerKickEvent groupPlayerKickEvent = new GroupPlayerKickEvent(group, dPlayer, GroupPlayerKickEvent.Cause.TIME_EXPIRED); Bukkit.getServer().getPluginManager().callEvent(groupPlayerKickEvent); if (!groupPlayerKickEvent.isCancelled()) { MessageUtil.broadcastMessage(DMessage.PLAYER_TIME_KICK.getMessage(player.getName())); dPlayer.leave(); } cancel(); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/player/groupadapter/PartiesAdapter.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.player.groupadapter; import com.alessiodp.parties.api.Parties; import com.alessiodp.parties.api.events.bukkit.party.BukkitPartiesPartyPreDeleteEvent; import com.alessiodp.parties.api.events.bukkit.party.BukkitPartiesPartyPreRenameEvent; import com.alessiodp.parties.api.events.bukkit.player.BukkitPartiesPlayerPostJoinEvent; import com.alessiodp.parties.api.events.bukkit.player.BukkitPartiesPlayerPostLeaveEvent; import com.alessiodp.parties.api.interfaces.PartiesAPI; import com.alessiodp.parties.api.interfaces.Party; import com.alessiodp.parties.api.interfaces.PartyPlayer; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GroupAdapter; import de.erethon.dungeonsxl.api.player.PlayerGroup; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.scheduler.BukkitRunnable; /** * This class may be used as a reference for implementations of the GroupAdapter API. * * @author Daniel Saukel */ public class PartiesAdapter extends GroupAdapter implements Listener { private PartiesAPI partiesAPI; public PartiesAdapter(DungeonsAPI api) { super(api); Bukkit.getPluginManager().registerEvents(this, api); partiesAPI = Parties.getApi(); } @Override public PlayerGroup createDungeonGroup(Party eGroup) { PlayerGroup dGroup = dxl.createGroup(Bukkit.getPlayer(eGroup.getLeader()), eGroup.getName()); eGroup.getOnlineMembers().forEach(p -> dGroup.addMember(Bukkit.getPlayer(p.getPlayerUUID()), false)); groups.put(dGroup, eGroup); return dGroup; } @Override public Party getExternalGroup(Player member) { PartyPlayer pPlayer = partiesAPI.getPartyPlayer(member.getUniqueId()); if (pPlayer == null) { return null; } return partiesAPI.getParty(pPlayer.getPartyName()); } @Override public int getGroupOnlineSize(Party eGroup) { return eGroup.getOnlineMembers().size(); } @Override public boolean isExternalGroupMember(Party eGroup, Player player) { if (eGroup == null) { return false; } return eGroup.getMembers().contains(player.getUniqueId()); } @EventHandler public void onDeletion(BukkitPartiesPartyPreDeleteEvent event) { PlayerGroup dGroup = getDungeonGroup(event.getParty()); if (dGroup != null) { groups.remove(dGroup); dGroup.delete(); } } @EventHandler public void onRename(BukkitPartiesPartyPreRenameEvent event) { PlayerGroup group = getDungeonGroup(event.getParty()); if (group == null) { return; } group.setName(event.getNewPartyName()); } @EventHandler public void onJoin(BukkitPartiesPlayerPostJoinEvent event) { new BukkitRunnable() { @Override public void run() { PlayerGroup group = getDungeonGroup(event.getParty()); if (group == null || group.isPlaying()) { return; } group.addMember(getPlayer(event.getPartyPlayer()), false); } }.runTask(dxl); } @EventHandler public void onLeave(BukkitPartiesPlayerPostLeaveEvent event) { new BukkitRunnable() { @Override public void run() { PlayerGroup group = getDungeonGroup(event.getParty()); if (group == null || group.isPlaying()) { return; } group.removeMember(getPlayer(event.getPartyPlayer()), false); } }.runTask(dxl); } private Player getPlayer(PartyPlayer player) { return Bukkit.getPlayer(player.getPlayerUUID()); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/FeeLevelRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.dungeonsxl.player.DPlayerData; import de.erethon.xlib.chat.MessageUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class FeeLevelRequirement implements Requirement { private DungeonsAPI api; private int fee; public FeeLevelRequirement(DungeonsAPI api) { this.api = api; } /* Getters and setters */ /** * @return the fee */ public int getFee() { return fee; } /** * @param fee the fee to set */ public void setFee(int fee) { this.fee = fee; } /* Actions */ @Override public void setup(ConfigurationSection config) { fee = config.getInt("feeLevel"); } @Override public boolean check(Player player) { return getRelevantLevel(player) >= fee; } @Override public BaseComponent[] getCheckMessage(Player player) { int level = getRelevantLevel(player); ChatColor color = level >= fee ? ChatColor.GREEN : ChatColor.DARK_RED; return new ComponentBuilder(DMessage.REQUIREMENT_FEE_LEVEL.getMessage() + ": ").color(ChatColor.GOLD) .append(String.valueOf(level)).color(color) .append("/" + fee).color(ChatColor.WHITE) .create(); } private int getRelevantLevel(Player player) { if (isKeepInventory(player)) { return player.getLevel(); } DGlobalPlayer dPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); return dPlayer.getData().getOldLevel(); } @Override public void demand(Player player) { DGamePlayer dPlayer = (DGamePlayer) api.getPlayerCache().getGamePlayer(player); if (dPlayer == null) { return; } DPlayerData data = dPlayer.getData(); data.setOldLevel(data.getOldLevel() - fee); data.getConfig().set(DPlayerData.PREFIX_STATE_PERSISTENCE + "oldLvl", data.getOldLevel()); data.save(); if (isKeepInventory(player)) { player.setLevel(player.getLevel() - fee); } MessageUtil.sendMessage(player, DMessage.REQUIREMENT_FEE.getMessage(fee + " levels")); } private boolean isKeepInventory(Player player) { Game game = api.getGame(player); if (game != null) { return game.getRules().getState(GameRule.KEEP_INVENTORY_ON_ENTER); } return true; } @Override public String toString() { return "FeeLevelRequirement{fee=" + fee + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/FeeMoneyRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.chat.MessageUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import net.milkbowl.vault.economy.Economy; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class FeeMoneyRequirement implements Requirement { private Economy econ; private double fee; public FeeMoneyRequirement(DungeonsAPI api) { econ = ((DungeonsXL) api).getXLib().getEconomyProvider(); } /* Getters and setters */ /** * @return the fee */ public double getFee() { return fee; } /** * @param fee the fee to set */ public void setFee(double fee) { this.fee = fee; } /* Actions */ @Override public void setup(ConfigurationSection config) { fee = config.getDouble("feeMoney"); } @Override public boolean check(Player player) { if (econ == null) { return true; } return econ.getBalance(player) >= fee; } @Override public BaseComponent[] getCheckMessage(Player player) { double money = econ.getBalance(player); ChatColor color = money >= fee ? ChatColor.GREEN : ChatColor.DARK_RED; return new ComponentBuilder(DMessage.REQUIREMENT_FEE_MONEY.getMessage() + ": ").color(ChatColor.GOLD) .append(String.valueOf(money)).color(color) .append("/" + fee).color(ChatColor.WHITE) .create(); } @Override public void demand(Player player) { if (econ == null) { return; } econ.withdrawPlayer(player, fee); MessageUtil.sendMessage(player, DMessage.REQUIREMENT_FEE.getMessage(econ.format(fee))); } @Override public String toString() { return "FeeMoneyRequirement{fee=" + fee + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/FinishedDungeonsRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.dungeonsxl.player.DPlayerData; import de.erethon.xlib.util.NumberUtil; import de.erethon.xlib.util.SimpleDateUtil; import java.util.ArrayList; import java.util.List; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * The dungeons that need to be finished before this one may be played. * * @author Daniel Saukel */ public class FinishedDungeonsRequirement implements Requirement { private static final long HOUR_IN_MILLIS = 3600000L; private class DungeonAndTime { String dungeon; double time = Double.NaN; @Override public String toString() { return dungeon + (!Double.isNaN(time) ? " " + DMessage.REQUIREMENT_FINISHED_DUNGEONS_WITHIN_TIME.getMessage(SimpleDateUtil.decimalToSexagesimalTime(time, 2)) : ""); } } private DungeonsAPI api; public FinishedDungeonsRequirement(DungeonsAPI api) { this.api = api; } /* * finishedDungeons: # all of: * - 7:vdf # vdf within the last 7 hours * - sku/test # one of sku and test */ private List> dungeons; @Override public void setup(ConfigurationSection config) { List entries = config.getStringList("finishedDungeons"); dungeons = new ArrayList<>(entries.size()); for (String entry : entries) { List alternatives = new ArrayList<>(); for (String string : entry.split("/")) { String[] split = string.split(":"); DungeonAndTime dat = new DungeonAndTime(); if (split.length > 1) { dat.time = NumberUtil.parseDouble(split[0], Double.NaN); dat.dungeon = split[1]; } else { dat.dungeon = split[0]; } alternatives.add(dat); } dungeons.add(alternatives); } } @Override public boolean check(Player player) { DPlayerData data = ((DGlobalPlayer) api.getPlayerCache().get(player)).getData(); allOf: for (List dats : dungeons) { oneOf: for (DungeonAndTime dat : dats) { if (Double.isNaN(dat.time)) { if (data.getTimeLastFinished(dat.dungeon) != -1) { continue allOf; } } else if (data.getTimeLastFinished(dat.dungeon) + dat.time * HOUR_IN_MILLIS >= System.currentTimeMillis()) { continue allOf; } return false; } } return true; } @Override public BaseComponent[] getCheckMessage(Player player) { DPlayerData data = ((DGlobalPlayer) api.getPlayerCache().get(player)).getData(); ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_FINISHED_DUNGEONS_NAME.getMessage() + ":\n").color(ChatColor.GOLD); boolean firstAnd = true; for (List dats : dungeons) { // GREEN if the dungeon is finished within the timeframe // WHITE if the dungeon is not finished, but part of a list where to have another one finished is sufficient // RED if no dungeon of the list is finished within the timeframe List finished = new ArrayList<>(); List notFinished = new ArrayList<>(); for (DungeonAndTime dat : dats) { if ((Double.isNaN(dat.time) && data.getTimeLastFinished(dat.dungeon) != -1) || !Double.isNaN(dat.time) && data.getTimeLastFinished(dat.dungeon) + dat.time * HOUR_IN_MILLIS >= System.currentTimeMillis()) { finished.add(dat); } else { notFinished.add(dat); } } if (!firstAnd) { builder.append(";\n" + DMessage.REQUIREMENT_FINISHED_DUNGEONS_AND.getMessage() + " ").color(ChatColor.GOLD); } else { firstAnd = false; } boolean firstOr = true; for (DungeonAndTime dat : finished) { if (!firstOr) { builder.append("\n" + DMessage.REQUIREMENT_FINISHED_DUNGEONS_OR.getMessage() + " ").color(ChatColor.GOLD); } else { firstOr = false; } builder.append(dat.toString()).color(ChatColor.GREEN); } for (DungeonAndTime dat : notFinished) { if (!firstOr) { builder.append("\n" + DMessage.REQUIREMENT_FINISHED_DUNGEONS_OR.getMessage() + " ").color(ChatColor.GOLD); } else { firstOr = false; } builder.append(dat.toString()).color(finished.isEmpty() ? ChatColor.DARK_RED : ChatColor.WHITE); } } return builder.create(); } @Override public void demand(Player player) { } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/ForbiddenItemsRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.XLib; import de.erethon.xlib.item.ExItem; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class ForbiddenItemsRequirement implements Requirement { private XLib xlib; private Map forbiddenItems = new HashMap<>(); public ForbiddenItemsRequirement(DungeonsAPI api) { xlib = api.getXLib(); } /* Getters and setters */ /** * @return the forbidden items (key) and if the check is deep (value) */ public Map getForbiddenItems() { return forbiddenItems; } /* Actions */ @Override public void setup(ConfigurationSection config) { for (String entry : config.getStringList("forbiddenItems")) { if (entry == null) { continue; } boolean star = !entry.contains("*"); entry = entry.replace("*", ""); ExItem item = xlib.getExItem(entry); if (item != null) { forbiddenItems.put(item, star); } } } @Override public boolean check(Player player) { for (ItemStack item : player.getInventory().getContents()) { if (item == null) { continue; } ExItem exItem = xlib.getExItem(item); for (Entry entry : forbiddenItems.entrySet()) { if (entry.getValue()) { if (exItem.isSubsumableUnder(entry.getKey())) { return false; } } else { if (exItem.equals(entry.getKey())) { return false; } } } } return true; } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_FORBIDDEN_ITEMS.getMessage() + ": ").color(ChatColor.GOLD); Set exInventory = new HashSet<>(); for (ItemStack item : player.getInventory().getContents()) { if (item != null) { exInventory.add(xlib.getExItem(item)); } } boolean first = true; for (Entry entry : forbiddenItems.entrySet()) { boolean contains = containsItem(exInventory, entry.getKey(), entry.getValue()); ChatColor color = contains ? ChatColor.DARK_RED : ChatColor.GREEN; if (!first) { builder.append(", ").color(ChatColor.WHITE); } else { first = false; } builder.append(entry.getKey().getName()).color(color); } return builder.create(); } private boolean containsItem(Set exInventory, ExItem forbiddenItem, boolean deepCheck) { for (ExItem item : exInventory) { if ((deepCheck && item.isSubsumableUnder(forbiddenItem)) || (!deepCheck && item.equals(forbiddenItem))) { return true; } } return false; } @Override public void demand(Player player) { } @Override public String toString() { return "ForbiddenItemsRequirement{items=" + forbiddenItems + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/GroupSizeRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class GroupSizeRequirement implements Requirement { private DungeonsAPI api; private int minimum; private int maximum; public GroupSizeRequirement(DungeonsAPI api) { this.api = api; } /** * @return the group minimum */ public int getMinimum() { return minimum; } /** * @param minimum the minimal group size to set */ public void setMinimum(int minimum) { this.minimum = minimum; } /** * @return the group size maximum */ public int getMaximum() { return maximum; } /** * @param maximum the maximal group size to set */ public void setMaximum(int maximum) { this.maximum = maximum; } /* Actions */ @Override public void setup(ConfigurationSection config) { minimum = config.getInt("groupSize.minimum"); maximum = config.getInt("groupSize.maximum"); } @Override public boolean check(Player player) { PlayerGroup group = api.getPlayerGroup(player); int size = group.getMembers().size(); return size >= minimum && size <= maximum; } @Override public BaseComponent[] getCheckMessage(Player player) { int size = api.getPlayerGroup(player).getMembers().size(); ChatColor color = size >= minimum && size <= maximum ? ChatColor.GREEN : ChatColor.DARK_RED; return new ComponentBuilder(DMessage.REQUIREMENT_GROUP_SIZE.getMessage() + ": ").color(ChatColor.GOLD) .append(String.valueOf(size)).color(color) .append("/" + minimum + "-" + maximum).color(ChatColor.WHITE) .create(); } @Override public void demand(Player player) { } @Override public String toString() { return "GroupSizeRequirement{minimum=" + minimum + "; maximum=" + maximum + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/KeyItemsRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.XLib; import de.erethon.xlib.item.ExItem; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class KeyItemsRequirement implements Requirement { private XLib xlib; private List keyItems; public KeyItemsRequirement(DungeonsAPI api) { xlib = api.getXLib(); } /* Getters and setters */ /** * @return the forbidden items */ public List getKeyItems() { return keyItems; } /* Actions */ @Override public void setup(ConfigurationSection config) { keyItems = xlib.deserializeExItemList(config, "keyItems"); } @Override public boolean check(Player player) { List keyItems = new ArrayList<>(this.keyItems); for (ItemStack item : player.getInventory().getContents()) { if (item == null) { continue; } keyItems.remove(xlib.getExItem(item)); } return keyItems.isEmpty(); } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_KEY_ITEMS.getMessage() + ": ").color(ChatColor.GOLD); Set exInventory = new HashSet<>(); for (ItemStack item : player.getInventory().getContents()) { if (item != null) { exInventory.add(xlib.getExItem(item)); } } boolean first = true; for (ExItem key : keyItems) { ChatColor color = exInventory.contains(key) ? ChatColor.GREEN : ChatColor.DARK_RED; if (!first) { builder.append(", ").color(ChatColor.WHITE); } else { first = false; } builder.append(key.getName()).color(color); } return builder.create(); } @Override public void demand(Player player) { } @Override public String toString() { return "KeyItemsRequirement{items=" + keyItems + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/PermissionRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import java.util.ArrayList; import java.util.List; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class PermissionRequirement implements Requirement { private List permissions = new ArrayList<>(); public PermissionRequirement(DungeonsAPI api) { } /* Getters and setters */ /** * @return the permission the player must have to play the dungeon */ public List getPermissions() { return permissions; } /** * @param permissions the permissions to set */ public void setPermissions(List permissions) { this.permissions = permissions; } /* Actions */ @Override public void setup(ConfigurationSection config) { permissions = config.getStringList("permission"); } @Override public boolean check(Player player) { for (String permission : permissions) { if (!player.hasPermission(permission)) { return false; } } return true; } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_PERMISSION.getMessage() + ": ").color(ChatColor.GOLD); boolean first = true; for (String node : permissions) { if (!first) { builder.append(", ").color(ChatColor.WHITE); } else { first = false; } builder.append(node).color(player.hasPermission(node) ? ChatColor.GREEN : ChatColor.DARK_RED); } return builder.create(); } @Override public void demand(Player player) { } @Override public String toString() { return "PermissionRequirement{permissions=" + permissions + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceFinishRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.xlib.util.SimpleDateUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * Time in hours when the game may be played again since it has been started the last time. * * @author Daniel Saukel */ public class TimeSinceFinishRequirement implements Requirement { private static final long HOUR_IN_MILLIS = 3600000L; private DungeonsAPI api; private double time; public TimeSinceFinishRequirement(DungeonsAPI api) { this.api = api; } @Override public void setup(ConfigurationSection config) { time = config.getDouble("timeSinceFinish", 0.0); } @Override public boolean check(Player player) { DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); return (globalPlayer.getData().getTimeLastFinished(globalPlayer.getGroup().getDungeon().getName()) + time * HOUR_IN_MILLIS) < System.currentTimeMillis(); } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_TIME_SINCE_FINISH .getMessage(SimpleDateUtil.decimalToSexagesimalTime(time, 2)) + ":\n").color(ChatColor.GOLD); DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); String dungeonName = globalPlayer.getGroup().getDungeon().getName(); long lastTime = globalPlayer.getData().getTimeLastFinished(dungeonName); if (lastTime == -1) { builder.append(DMessage.REQUIREMENT_TIME_SINCE_NEVER.getMessage()).color(ChatColor.GREEN); } else { ChatColor color = lastTime + time * HOUR_IN_MILLIS < System.currentTimeMillis() ? ChatColor.GREEN : ChatColor.DARK_RED; builder.append(SimpleDateUtil.ddMMMMyyyyhhmmss(lastTime)).color(color); } return builder.create(); } @Override public void demand(Player player) { } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/TimeSinceStartRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGlobalPlayer; import de.erethon.xlib.util.SimpleDateUtil; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * Time in hours when the game may be played again since it has been started the last time. * * @author Daniel Saukel */ public class TimeSinceStartRequirement implements Requirement { private static final long HOUR_IN_MILLIS = 3600000L; private DungeonsAPI api; private double time; public TimeSinceStartRequirement(DungeonsAPI api) { this.api = api; } @Override public void setup(ConfigurationSection config) { time = config.getDouble("timeSinceStart", 0.0); } @Override public boolean check(Player player) { DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); return (globalPlayer.getData().getTimeLastStarted(globalPlayer.getGroup().getDungeon().getName()) + time * HOUR_IN_MILLIS) < System.currentTimeMillis(); } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_TIME_SINCE_START .getMessage(SimpleDateUtil.decimalToSexagesimalTime(time, 2)) + ":\n").color(ChatColor.GOLD); DGlobalPlayer globalPlayer = (DGlobalPlayer) api.getPlayerCache().get(player); String dungeonName = globalPlayer.getGroup().getDungeon().getName(); long lastTime = globalPlayer.getData().getTimeLastStarted(dungeonName); if (lastTime == -1) { builder.append(DMessage.REQUIREMENT_TIME_SINCE_NEVER.getMessage()).color(ChatColor.GREEN); } else { ChatColor color = lastTime + time * HOUR_IN_MILLIS < System.currentTimeMillis() ? ChatColor.GREEN : ChatColor.DARK_RED; builder.append(SimpleDateUtil.ddMMMMyyyyhhmmss(lastTime)).color(color); } return builder.create(); } @Override public void demand(Player player) { } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/requirement/TimeframeRequirement.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.requirement; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Requirement; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.util.EnumUtil; import de.erethon.xlib.util.NumberUtil; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.chat.ComponentBuilder; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class TimeframeRequirement implements Requirement { public enum Weekday { SUNDAY, // *angry German noises* MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY; @Override public String toString() { return DMessage.valueOf("DAY_OF_WEEK_" + ordinal()).getMessage(); } } public static class Timeframe { private T start, end; public Timeframe(T start, T end) { this.start = start; this.end = end; } public T getStart() { return start; } public T getEnd() { return end; } } private List> weekdays = new ArrayList<>(); private List> daytimes = new ArrayList<>(); public TimeframeRequirement(DungeonsAPI api) { } @Override public void setup(ConfigurationSection config) { List dates = config.getStringList("timeframe"); for (String date : dates) { String[] time = date.split("-"); Weekday firstDay = EnumUtil.getEnumIgnoreCase(Weekday.class, time[0]); if (firstDay != null) { Weekday secondDay = firstDay; if (time.length == 2) { secondDay = EnumUtil.getEnumIgnoreCase(Weekday.class, time[1]); } if (secondDay.ordinal() >= firstDay.ordinal()) { weekdays.add(new Timeframe<>(firstDay, secondDay)); } } if (time.length < 2) { continue; } int firstHour = NumberUtil.parseInt(time[0], 0); int secondHour = NumberUtil.parseInt(time[1], -1); if (secondHour > firstHour) { daytimes.add(new Timeframe<>(firstHour, secondHour)); } } } @Override public boolean check(Player player) { boolean match = weekdays.isEmpty(); for (Timeframe timeframe : weekdays) { if (isInDayTimeframe(timeframe)) { match = true; break; } } if (!match) { return false; } match = daytimes.isEmpty(); for (Timeframe timeframe : daytimes) { if (isInHourTimeframe(timeframe)) { match = true; break; } } return match; } private boolean isInDayTimeframe(Timeframe timeframe) { int day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) - 1; // Calendar days start with 1... return day >= timeframe.getStart().ordinal() && day <= timeframe.getEnd().ordinal(); } private boolean isInHourTimeframe(Timeframe timeframe) { int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY); return hour >= timeframe.getStart() && hour < timeframe.getEnd(); } @Override public BaseComponent[] getCheckMessage(Player player) { ComponentBuilder builder = new ComponentBuilder(DMessage.REQUIREMENT_TIMEFRAME.getMessage() + ": ").color(ChatColor.GOLD); boolean first = true; for (Timeframe timeframe : weekdays) { ChatColor color = isInDayTimeframe(timeframe) ? ChatColor.GREEN : ChatColor.DARK_RED; if (!first) { builder.append(" & ").color(ChatColor.WHITE); } else { first = false; } if (timeframe.getStart() != timeframe.getEnd()) { builder.append(timeframe.getStart() + "-" + timeframe.getEnd()).color(color); } else { builder.append(timeframe.getStart().toString()).color(color); } } first = true; for (Timeframe timeframe : daytimes) { ChatColor color = isInHourTimeframe(timeframe) ? ChatColor.GREEN : ChatColor.DARK_RED; if (!first) { builder.append(" & ").color(ChatColor.WHITE); } else { first = false; if (!weekdays.isEmpty()) { builder.append(" | ").color(ChatColor.WHITE); } } builder.append(timeframe.getStart() + "-" + timeframe.getEnd()).color(color); } return builder.create(); } @Override public void demand(Player player) { } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/reward/ItemReward.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.reward; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.Reward; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class ItemReward implements Reward { private DungeonsAPI api; private List items = new ArrayList<>(); public ItemReward(DungeonsAPI api) { this.api = api; } /* Getters and setters */ /** * @return the reward items */ public List getItems() { return items; } /** * @param items the reward items to set */ public void setItems(List items) { this.items = items; } /** * @param items the reward items to add */ public void addItems(ItemStack... items) { this.items.addAll(Arrays.asList(items)); } /** * @param items the reward items to remove */ public void removeItems(ItemStack... items) { this.items.addAll(Arrays.asList(items)); } /* Actions */ @Override public void giveTo(Player player) { api.getPlayerCache().get(player).setRewardItems(items); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/reward/LevelReward.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.reward; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class LevelReward implements Reward { private int levels; /** * @return the levels */ public int getLevels() { return levels; } /** * @param levels the levels to add */ public void addLevels(int levels) { this.levels += levels; } /** * @param levels the levels to set */ public void setLevels(int levels) { this.levels = levels; } @Override public void giveTo(Player player) { if (levels == 0) { return; } player.setLevel(player.getLevel() + levels); MessageUtil.sendMessage(player, DMessage.REWARD_GENERAL.getMessage(levels + " levels")); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/reward/MoneyReward.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.reward; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.xlib.chat.MessageUtil; import net.milkbowl.vault.economy.Economy; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class MoneyReward implements Reward { private Economy econ; private double money; public MoneyReward(Economy econ) { this.econ = econ; } /** * @return the money */ public double getMoney() { return money; } /** * @param money the money to add */ public void addMoney(double money) { this.money += money; } /** * @param money the money to set */ public void setMoney(double money) { this.money = money; } @Override public void giveTo(Player player) { if (econ == null || money == 0) { return; } econ.depositPlayer(player, money); MessageUtil.sendMessage(player, DMessage.REWARD_GENERAL.getMessage(econ.format(money))); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/reward/RewardListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.reward; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.player.DPlayerListener; import de.erethon.dungeonsxl.util.ContainerAdapter; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.RewardChest; import de.erethon.xlib.gui.PaginatedInventoryGUI; import de.erethon.xlib.gui.component.InventoryButton; import de.erethon.xlib.gui.layout.PaginatedFlowInventoryLayout; import de.erethon.xlib.gui.layout.PaginatedInventoryLayout.PaginationButtonPosition; import de.erethon.xlib.item.VanillaItem; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryType; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryView; import org.bukkit.inventory.ItemStack; /** * @author Frank Baumann, Daniel Saukel */ public class RewardListener implements Listener { private DungeonsXL plugin; public RewardListener(DungeonsXL plugin) { this.plugin = plugin; } /*@EventHandler public void onInventoryClose(InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player)) { return; } Player player = (Player) event.getPlayer(); for (DLootInventory inventory : plugin.getDLootInventories()) { if (PageGUI.getByInventory() != inventory.getInventory()) { continue; } if (System.currentTimeMillis() - inventory.getTime() <= 500) { continue; } for (ItemStack istack : inventory.getInventory().getContents()) { if (istack != null) { player.getWorld().dropItem(player.getLocation(), istack); } } plugin.getDLootInventories().remove(inventory); } }*/ @EventHandler public void onInventoryOpen(InventoryOpenEvent event) { if (!(event.getPlayer() instanceof Player)) { return; } InventoryView inventory = event.getView(); DGameWorld gameWorld = (DGameWorld) plugin.getGameWorld(event.getPlayer().getWorld()); if (gameWorld == null) { return; } if (!(ContainerAdapter.isValidContainer(inventory.getTopInventory()))) { return; } for (RewardChest rewardChest : gameWorld.getRewardChests()) { if (!rewardChest.getBlock().equals(ContainerAdapter.getHolderBlock(inventory.getTopInventory().getHolder()))) { continue; } rewardChest.onOpen((Player) event.getPlayer()); event.setCancelled(true); break; } if (!plugin.getMainConfig().getOpenInventories() && !DPermission.hasPermission(event.getPlayer(), DPermission.INSECURE)) { World world = event.getPlayer().getWorld(); if (event.getInventory().getType() != InventoryType.CREATIVE && plugin.getEditWorld(world) != null) { event.setCancelled(true); } } } @EventHandler public void onPlayerMove(PlayerMoveEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player)) { return; } GlobalPlayer dPlayer = plugin.getPlayerCache().get(player); World world = player.getWorld(); Location location = player.getLocation(); if (plugin.getInstanceWorld(world) != null) { return; } Block block = location.getBlock(); if (dPlayer.hasRewardItemsLeft() && !VanillaItem.NETHER_PORTAL.is(block.getRelative(0, 1, 0)) && !VanillaItem.NETHER_PORTAL.is(block.getRelative(0, -1, 0)) && !VanillaItem.NETHER_PORTAL.is(block.getRelative(1, 0, 0)) && !VanillaItem.NETHER_PORTAL.is(block.getRelative(-1, 0, 0)) && !VanillaItem.NETHER_PORTAL.is(block.getRelative(0, 0, 1)) && !VanillaItem.NETHER_PORTAL.is(block.getRelative(0, 0, -1))) { PaginatedInventoryGUI lootInventory = new PaginatedInventoryGUI(DMessage.PLAYER_TREASURES.getMessage()); PaginatedFlowInventoryLayout layout = new PaginatedFlowInventoryLayout(lootInventory, 54, PaginationButtonPosition.BOTTOM); layout.setSwitchButtonLinePlaceholdersEnabled(true); lootInventory.setCloseListener(e -> { for (Inventory inventory : lootInventory.getOpenedInventories()) { for (int i = 0; i < 45; i++) { ItemStack item = inventory.getItem(i); if (item != null) { world.dropItem(location, item); } } } }); lootInventory.setLayout(layout); lootInventory.register(); for (ItemStack item : dPlayer.getRewardItems()) { if (item != null) { InventoryButton button = new InventoryButton(item); button.setStealable(true); lootInventory.add(button); } } lootInventory.open(player); dPlayer.setRewardItems(null); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/DSignListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPlayerListener; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import org.bukkit.ChatColor; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; /** * @author Frank Baumann, Daniel Saukel */ public class DSignListener implements Listener { private DungeonsAPI api; public DSignListener(DungeonsAPI api) { this.api = api; } @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player)) { return; } Block clickedBlock = event.getClickedBlock(); if (clickedBlock == null) { return; } GamePlayer dPlayer = api.getPlayerCache().getGamePlayer(player); if (dPlayer == null) { return; } DGameWorld gameWorld = (DGameWorld) dPlayer.getGameWorld(); if (gameWorld == null) { return; } InteractTrigger trigger = InteractTrigger.getByBlock(clickedBlock, gameWorld); if (trigger == null) { return; } if (event.getAction() == Action.LEFT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_BLOCK) { trigger.trigger(true, player); } } @EventHandler public void onSignChange(SignChangeEvent event) { String[] lines = event.getLines(); if (lines[0].length() < 3 || !lines[0].startsWith("[")) { return; } Player player = event.getPlayer(); Block block = event.getBlock(); BlockState state = block.getState(); if (!(state instanceof Sign)) { return; } Sign sign = (Sign) state; EditWorld editWorld = api.getEditWorld(sign.getWorld()); if (editWorld == null) { return; } // Override sign plugins color codes etc. sign.setLine(0, lines[0]); sign.setLine(1, lines[1]); sign.setLine(2, lines[2]); sign.setLine(3, lines[3]); if (DungeonsXL.LEGACY_SIGNS.containsKey(lines[0].substring(1, lines[0].length() - 1).toUpperCase())) { MessageUtil.sendMessage(player, ChatColor.RED + "https://erethon.de/resources/dxl-signs/deprecated.gif"); MessageUtil.sendMessage(player, ChatColor.LIGHT_PURPLE + "https://github.com/DRE2N/DungeonsXL/wiki/Legacy-support#updating"); event.setCancelled(true); event.getBlock().setType(VanillaItem.AIR.getMaterial()); return; } DungeonSign dsign = editWorld.createDungeonSign(sign, sign.getLines()); if (dsign == null) { return; } if (!player.hasPermission(dsign.getBuildPermission())) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_PERMISSIONS.getMessage()); return; } if (dsign.validate()) { editWorld.registerSign(block); MessageUtil.sendMessage(player, DMessage.PLAYER_SIGN_CREATED.getMessage()); } else { editWorld.removeDungeonSign(block); MessageUtil.sendMessage(player, DMessage.ERROR_SIGN_WRONG_FORMAT.getMessage()); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/LocationSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.xlib.util.BlockUtil; import org.bukkit.Location; /** * @author Daniel Saukel */ public interface LocationSign extends DungeonSign { @Override default void initialize() { double x = getSign().getX() + 0.5; double y = getSign().getY(); double z = getSign().getZ() + 0.5; float yaw = BlockUtil.blockFaceToYaw(DungeonsXL.BLOCK_ADAPTER.getFacing(getSign().getBlock()).getOppositeFace()); float pitch = 0; setTargetLocation(new Location(getGameWorld().getWorld(), x, y, z, yaw, pitch)); } Location getTargetLocation(); void setTargetLocation(Location location); } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/ActionBarSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class ActionBarSign extends MessageSign { public ActionBarSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "ActionBar"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".actionbar"; } @Override public void sendMessage(Player player) { MessageUtil.sendActionBarMessage(player, text); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/BossShopSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.xlib.chat.MessageUtil; import org.black_ixx.bossshop.BossShop; import org.black_ixx.bossshop.core.BSShop; import org.bukkit.Bukkit; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class BossShopSign extends Button { private BossShop bossShop; private String shopName; public BossShopSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public String getShopName() { return shopName; } public void setShopName(String name) { shopName = name; } @Override public String getName() { return "BossShop"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".bossshop"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } @Override public void initialize() { if (Bukkit.getPluginManager().isPluginEnabled("BossShopPro")) { bossShop = (BossShop) Bukkit.getPluginManager().getPlugin("BossShopPro"); } else { bossShop = (BossShop) Bukkit.getPluginManager().getPlugin("BossShop"); } if (bossShop == null) { markAsErroneous("BossShop not enabled"); return; } else if (bossShop.getAPI().getShop(getLine(1)) == null) { markAsErroneous("No such BossShop"); return; } shopName = getLine(1); if (!getTriggers().isEmpty()) { setToAir(); return; } InteractTrigger.addDefault(api, this, getLine(1), getLine(2)); } @Override public boolean push(Player player) { openShop(player, shopName); return true; } public void openShop(Player player, String shopName) { BSShop shop = bossShop.getAPI().getShop(shopName); if (shop != null) { bossShop.getAPI().openShop(player, shop); } else { MessageUtil.sendMessage(player, DMessage.ERROR_NO_SUCH_SHOP.getMessage(shopName)); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/ChatMessageSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class ChatMessageSign extends MessageSign { public ChatMessageSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "MSG"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".msg"; } @Override public void sendMessage(Player player) { MessageUtil.sendMessage(player, text); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/CheckpointSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import java.util.ArrayList; import java.util.List; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class CheckpointSign extends Button { private List done = new ArrayList<>(); public CheckpointSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Checkpoint"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".checkpoint"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { } @Override public void push() { for (InstancePlayer instancePlayer : getGameWorld().getPlayers()) { GamePlayer gamePlayer = (GamePlayer) instancePlayer; if (done.contains(gamePlayer)) { continue; } gamePlayer.setLastCheckpoint(getSign().getLocation()); gamePlayer.sendMessage(DMessage.PLAYER_CHECKPOINT_REACHED.getMessage()); } getGameWorld().removeDungeonSign(this); } @Override public boolean push(Player player) { GamePlayer gamePlayer = api.getPlayerCache().getGamePlayer(player); if (!done.contains(gamePlayer)) { done.add(gamePlayer); gamePlayer.setLastCheckpoint(getSign().getLocation()); MessageUtil.sendMessage(player, DMessage.PLAYER_CHECKPOINT_REACHED.getMessage()); } if (done.size() >= getGameWorld().getPlayers().size()) { getGameWorld().removeDungeonSign(this); } return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/ClassesSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerClass; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class ClassesSign extends Button { private PlayerClass playerClass; public ClassesSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); playerClass = api.getClassRegistry().get(sign.getLine(1)); } public PlayerClass getPlayerClass() { return playerClass; } public void setPlayerClass(PlayerClass playerClass) { this.playerClass = playerClass; } @Override public String getName() { return "Classes"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".classes"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return api.getClassRegistry().get(getLine(1)) != null; } @Override public void initialize() { if (playerClass != null) { InteractTrigger.addDefault(api, this, playerClass.getName(), ""); getGameWorld().setClassesEnabled(true); } else { markAsErroneous("No such class"); } } @Override public boolean push(Player player) { GamePlayer gamePlayer = api.getPlayerCache().getGamePlayer(player); gamePlayer.setPlayerClass(playerClass); return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/EndSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.GameGoal; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.xlib.chat.MessageUtil; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class EndSign extends Button { private ResourceWorld floor; public EndSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public ResourceWorld getFloor() { return floor; } public void setFloor(ResourceWorld floor) { this.floor = floor; } @Override public String getName() { return "End"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".end"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } @Override public void initialize() { GameGoal goal = getGame().getRules().getState(GameRule.GAME_GOAL); if (goal.getType() != GameGoal.Type.END) { setToAir(); MessageUtil.log(api, "&4An end sign in the dungeon " + getGame().getDungeon().getName() + " is ignored because the game goal is " + goal.toString()); return; } if (!getLine(1).isEmpty()) { floor = api.getMapRegistry().get(getLine(1)); } if (!getTriggers().isEmpty()) { setToAir(); return; } String line1, line2; Dungeon dungeon = getGame().getDungeon(); if (dungeon.isMultiFloor() && !getGame().getUnplayedFloors().isEmpty() && getGameWorld().getResource() != dungeon.getEndFloor()) { line1 = DMessage.SIGN_FLOOR_1.getMessage(); if (floor == null) { line2 = DMessage.SIGN_FLOOR_2.getMessage(); } else { line2 = floor.getName().replace("_", " "); } } else { line1 = DMessage.SIGN_END.getMessage(); line2 = ""; } InteractTrigger.addDefault(api, this, line1, line2); } @Override public boolean push(Player player) { DGamePlayer dPlayer = (DGamePlayer) api.getPlayerCache().getGamePlayer(player); if (dPlayer == null) { return true; } // TODO: Group with 2 players, player A finishs, player B leaves if (dPlayer.isFinished()) { return true; } new BukkitRunnable() { @Override public void run() { dPlayer.finishFloor((DResourceWorld) floor); } }.runTaskLater(api, 1L); return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/LeaveSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.event.group.GroupPlayerLeaveEvent; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import org.bukkit.Bukkit; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class LeaveSign extends Button { public LeaveSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Leave"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".leave"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } @Override public void initialize() { if (!getTriggers().isEmpty()) { setToAir(); } else { InteractTrigger.addDefault(api, this, DMessage.SIGN_LEAVE.getMessage(), ""); } } @Override public boolean push(Player player) { GamePlayer gamePlayer = api.getPlayerCache().getGamePlayer(player); if (gamePlayer != null) { GroupPlayerLeaveEvent event = new GroupPlayerLeaveEvent(gamePlayer.getGroup(), gamePlayer); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return false; } gamePlayer.leave(); } return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/LivesModifierSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.EnumUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class LivesModifierSign extends Button { public enum Target { GAME, GROUP, PLAYER, } private int lives; private Target target; public LivesModifierSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public int getLives() { return lives; } public void setLives(int lives) { this.lives = lives; } @Override public String getName() { return "Lives"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".lives"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return NumberUtil.parseInt(getLine(1)) != 0; } @Override public void initialize() { lives = NumberUtil.parseInt(getLine(1)); if (EnumUtil.isValidEnum(Target.class, getLine(2).toUpperCase())) { target = Target.valueOf(getLine(2).toUpperCase()); } else { target = Target.PLAYER; } } @Override public boolean push(Player player) { switch (target) { case GAME: for (Player gamePlayer : getGame().getPlayers()) { GamePlayer dPlayer = api.getPlayerCache().getGamePlayer(player); if (gamePlayer != null) { modifyLives(dPlayer); } } break; case GROUP: modifyLives(api.getPlayerGroup(player)); break; case PLAYER: modifyLives(api.getPlayerCache().getGamePlayer(player)); } return true; } public void modifyLives(GamePlayer dPlayer) { dPlayer.setLives(dPlayer.getLives() + lives); if (lives > 0) { MessageUtil.sendMessage(dPlayer.getPlayer(), DMessage.PLAYER_LIVES_ADDED.getMessage(String.valueOf(lives))); } else { MessageUtil.sendMessage(dPlayer.getPlayer(), DMessage.PLAYER_LIVES_REMOVED.getMessage(String.valueOf(-1 * lives))); } if (dPlayer.getLives() <= 0) { dPlayer.kill(); } } public void modifyLives(PlayerGroup group) { group.setLives(group.getLives() + lives); if (lives > 0) { group.sendMessage(DMessage.GROUP_LIVES_ADDED.getMessage(String.valueOf(lives))); } else { group.sendMessage(DMessage.GROUP_LIVES_REMOVED.getMessage(String.valueOf(-1 * lives))); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/MessageSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.xlib.util.NumberUtil; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public abstract class MessageSign extends Button { protected String text = "UNKNOWN MESSAGE"; public MessageSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public String getText() { return text; } public void setText(String text) { this.text = text; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return !getLine(1).isEmpty(); } @Override public void initialize() { String text = getGameWorld().getDungeon().getRules().getState(GameRule.MESSAGES).get(NumberUtil.parseInt(getLine(1))); if (text != null) { this.text = text; } else { markAsErroneous("Unknown message, ID: " + getLine(1)); } } @Override public boolean push(Player player) { sendMessage(player); return true; } @Override public void push() { for (Player player : getGameWorld().getWorld().getPlayers()) { sendMessage(player); } getGameWorld().removeDungeonSign(this); } public abstract void sendMessage(Player player); } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/ReadySign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.xlib.util.NumberUtil; import de.erethon.xlib.util.ProgressBar; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class ReadySign extends Button { private double autoStart = -1; private boolean triggered = false; private ProgressBar bar; public ReadySign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public double getTimeToAutoStart() { return autoStart; } public void setTimeToAutoStart(double time) { autoStart = time; } @Override public String getName() { return "Ready"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".ready"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } @Override public void initialize() { ((DGameWorld) getGameWorld()).setReadySign(this); if (!getLine(2).isEmpty()) { autoStart = NumberUtil.parseDouble(getLine(2), -1); } if (!getTriggers().isEmpty()) { setToAir(); return; } InteractTrigger.addDefault(api, this, DMessage.SIGN_READY.getMessage(), ""); } @Override public void push() { if (getGame() == null) { return; } if (bar != null) { bar.cancel(); } readyAll(); } @Override public boolean push(Player player) { GamePlayer gamePlayer = api.getPlayerCache().getGamePlayer(player); ready(gamePlayer); if (!triggered && autoStart >= 0) { triggered = true; if (gamePlayer != null && !gamePlayer.getGroup().isPlaying()) { bar = new ProgressBar(getGame().getPlayers(), (int) Math.ceil(autoStart)) { @Override public void onFinish() { push(); } }; bar.send(api); } } return true; } private void readyAll() { for (PlayerGroup group : getGame().getGroups()) { for (UUID memberId : group.getMembers()) { Player player = Bukkit.getPlayer(memberId); if (player != null) { GamePlayer gamePlayer = api.getPlayerCache().getGamePlayer(player); if (gamePlayer == null) { gamePlayer = new DGamePlayer((DungeonsXL) api, player, getGameWorld()); } ready(gamePlayer); } else { group.getMembers().remove(memberId); } } } } private void ready(GamePlayer player) { if (player == null) { return; } boolean wasReady = player.isReady(); if (!getGameWorld().areClassesEnabled() || player.getPlayerClass() != null) { if (player.ready()) { getGame().start(); if (bar != null) { bar.cancel(); } } } if (!wasReady) { if (player.isReady()) { player.sendMessage(DMessage.PLAYER_READY.getMessage()); } else if (getGameWorld().areClassesEnabled()) { player.sendMessage(DMessage.ERROR_READY.getMessage()); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/ResourcePackSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class ResourcePackSign extends Button { private String resourcePack; public ResourcePackSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public String getResourcePack() { return resourcePack; } public void setExternalMob(String resourcePack) { this.resourcePack = resourcePack; } @Override public String getName() { return "ResourcePack"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".resourcepack"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return ((DungeonsXL) api).getMainConfig().getResourcePacks().get(getLine(1)) != null || getLine(1).equalsIgnoreCase("reset"); } @Override public void initialize() { Object url = null; if (getLine(1).equalsIgnoreCase("reset")) { // Placeholder to reset to default url = "http://google.com"; } else { url = ((DungeonsXL) api).getMainConfig().getResourcePacks().get(getLine(1)); } if (url instanceof String) { resourcePack = (String) url; } else { markAsErroneous("Unknown resourcepack format"); return; } if (!getTriggers().isEmpty()) { setToAir(); return; } InteractTrigger.addDefault(api, this, DMessage.SIGN_RESOURCE_PACK.getMessage(), getLine(1)); } @Override public boolean push(Player player) { player.setResourcePack(resourcePack); return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/SoundMessageSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.util.EnumUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.SoundCategory; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class SoundMessageSign extends Button { private String sound; private SoundCategory category; private float volume; private float pitch; public SoundMessageSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "SoundMSG"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".soundmsg"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { if (getLine(1).isEmpty()) { markAsErroneous("1. Line is empty; expected input: sound name"); return false; } return true; } @Override public void initialize() { sound = getLine(1); if (getLine(2).isEmpty()) { return; } String[] args = getLine(2).split(","); if (args.length >= 1 && args.length != 2 && Version.isAtLeast(Version.MC1_11)) { category = EnumUtil.getEnumIgnoreCase(SoundCategory.class, args[0]); if (category == null) { category = SoundCategory.MASTER; } } if (args.length == 2) { volume = (float) NumberUtil.parseDouble(args[0], 5.0); pitch = (float) NumberUtil.parseDouble(args[1], 1.0); } else if (args.length == 3) { volume = (float) NumberUtil.parseDouble(args[1], 5.0); pitch = (float) NumberUtil.parseDouble(args[2], 1.0); } } @Override public void push() { for (Player player : getGameWorld().getWorld().getPlayers()) { playSound(player); } getGameWorld().removeDungeonSign(this); } @Override public boolean push(Player player) { playSound(player); return true; } private void playSound(Player player) { if (Version.isAtLeast(Version.MC1_11)) { player.playSound(getSign().getLocation(), sound, category, volume, pitch); } else { player.playSound(getSign().getLocation(), sound, volume, pitch); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/TeleportSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.sign.LocationSign; import de.erethon.xlib.util.BlockUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Location; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Milan Albrecht, Daniel Saukel */ public class TeleportSign extends Button implements LocationSign { private Location targetLocation; public TeleportSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public Location getTargetLocation() { return targetLocation; } @Override public void setTargetLocation(Location location) { this.targetLocation = location; } @Override public String getName() { return "Teleport"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".teleport"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { for (int i = 1; i <= 2; i++) { if (!getLine(i).isEmpty()) { if (BlockUtil.lettersToYaw(getLine(i)) == -1) { String[] loc = getLine(i).split(","); if (loc.length != 3) { return false; } } } } return true; } @Override public void initialize() { LocationSign.super.initialize(); for (int i = 1; i <= 2; i++) { if (getLine(i).isEmpty()) { continue; } Integer yaw = BlockUtil.lettersToYaw(getLine(i)); if (yaw != null) { targetLocation.setYaw(yaw); } else { String[] loc = getLine(i).split(","); if (loc.length == 3) { double x = NumberUtil.parseDouble(loc[0]); double y = NumberUtil.parseDouble(loc[1]); double z = NumberUtil.parseDouble(loc[2]); // If number is even, add +0.5 to teleport to the middle of the block if (!loc[0].contains(".")) { x += 0.5; } if (!loc[2].contains(".")) { z += 0.5; } targetLocation.setX(x); targetLocation.setY(y); targetLocation.setZ(z); } } } } @Override public boolean push(Player player) { if (targetLocation != null) { player.teleport(targetLocation); return true; } else { return false; } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/TitleSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.util.NumberUtil; import java.util.Map; import org.bukkit.block.Sign; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class TitleSign extends MessageSign { private String title, subtitle; private int fadeIn = 10, stay = 70, fadeOut = 20; public TitleSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Title"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".title"; } @Override public void initialize() { String[] line1 = getLine(1).split(","); Map messages = getGameWorld().getDungeon().getRules().getState(GameRule.MESSAGES); int id0 = NumberUtil.parseInt(line1[0], -1); title = messages.get(id0); if (title == null) { markAsErroneous("Unknown message, ID: " + getLine(1)); return; } if (line1.length > 1) { int id1 = NumberUtil.parseInt(line1[1], -1); subtitle = messages.get(id1); if (subtitle == null) { markAsErroneous("Unknown message, ID: " + getLine(1)); return; } } if (subtitle == null) { subtitle = ""; } if (getLine(2).isEmpty()) { return; } String[] line2 = getLine(2).split(","); if (line2.length != 3) { return; } fadeIn = NumberUtil.parseInt(line2[0], fadeIn); stay = NumberUtil.parseInt(line2[1], stay); fadeOut = NumberUtil.parseInt(line2[2], fadeOut); } @Override public void sendMessage(Player player) { MessageUtil.sendTitleMessage(player, title, subtitle, fadeIn, stay, fadeOut); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/button/WaveSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.button; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Button; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.dungeon.DGame; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.xlib.util.NumberUtil; import org.bukkit.block.Sign; /** * @author Frank Baumann, Daniel Saukel */ public class WaveSign extends Button { private double mobCountIncreaseRate; private boolean teleport; public WaveSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public double getMobCountIncreaseRate() { return mobCountIncreaseRate; } public void setMobCountIncreaseRate(double mobCountIncreaseRate) { this.mobCountIncreaseRate = mobCountIncreaseRate; } public boolean getTeleport() { return teleport; } public void setTeleport(boolean teleport) { this.teleport = teleport; } @Override public String getName() { return "Wave"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".wave"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } @Override public void initialize() { if (!getLine(1).isEmpty()) { mobCountIncreaseRate = NumberUtil.parseDouble(getLine(1), 2); } if (!getLine(2).isEmpty()) { teleport = getLine(2).equals("+") || getLine(2).equals("true"); } if (!getTriggers().isEmpty()) { setToAir(); return; } InteractTrigger.addDefault(api, this, DMessage.SIGN_WAVE_1.getMessage(), DMessage.SIGN_WAVE_2.getMessage()); } @Override public void push() { ((DGame) getGame()).finishWave(mobCountIncreaseRate, teleport); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/BedSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.TeamBed; import de.erethon.xlib.category.Category; import de.erethon.xlib.util.NumberUtil; import org.bukkit.block.Block; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class BedSign extends Passive { private int team; public BedSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Bed"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".bed"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return NumberUtil.parseInt(getLine(1), -1) != -1; } @Override public void initialize() { this.team = NumberUtil.parseInt(getLine(1)); Block block = BlockUtilCompat.getAttachedBlock(getSign().getBlock()); if (Category.BEDS.containsBlock(block)) { if (getGame().getGroups().size() > team) { ((DGameWorld) getGameWorld()).addGameBlock(new TeamBed(api, block, (DGroup) getGame().getGroups().get(team))); } } else { markAsErroneous("No bed attached"); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/ChestSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.util.ContainerAdapter; import de.erethon.xlib.loottable.LootTable; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.inventory.ItemStack; /** * @author Frank Baumann, Daniel Saukel */ public abstract class ChestSign extends Passive { protected Block chest; protected ItemStack[] chestContent; protected LootTable lootTable; protected ChestSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public ItemStack[] getChestContents() { if (chestContent == null) { checkChest(); } return chestContent; } public void setChestContents(ItemStack[] items) { chestContent = items; } public LootTable getLootTable() { return lootTable; } public void setLootTable(LootTable lootTable) { this.lootTable = lootTable; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } /** * Checks for a chest next to the sign and sets the reward to its contents. */ protected void checkChest() { Block sign = getSign().getBlock(); for (int i = -1; i <= 1; i++) { Block xRelative = sign.getRelative(i, 0, 0); Block yRelative = sign.getRelative(0, i, 0); Block zRelative = sign.getRelative(0, 0, i); if (ContainerAdapter.isValidContainer(xRelative)) { if (chestContent == null) { chestContent = ContainerAdapter.getBlockInventory(xRelative).getContents(); } chest = xRelative; } else if (ContainerAdapter.isValidContainer(yRelative)) { if (chestContent == null) { chestContent = ContainerAdapter.getBlockInventory(yRelative).getContents(); } chest = yRelative; } else if (ContainerAdapter.isValidContainer(zRelative)) { if (chestContent == null) { chestContent = ContainerAdapter.getBlockInventory(zRelative).getContents(); } chest = zRelative; } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/DungeonChestSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.ContainerAdapter; import de.erethon.xlib.item.VanillaItem; import java.util.Arrays; import java.util.List; import org.bukkit.block.Sign; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class DungeonChestSign extends ChestSign { public DungeonChestSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "DungeonChest"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".dungeonchest"; } @Override public void initialize() { // For consistency with reward chests but also for intuitiveness, both lines should be possible if (!getLine(1).isEmpty()) { lootTable = api.getXLib().getLootTable(getLine(1)); } if (!getLine(2).isEmpty()) { lootTable = api.getXLib().getLootTable(getLine(2)); } checkChest(); if (chest != null) { setToAir(); } else { getSign().getBlock().setType(VanillaItem.CHEST.getMaterial()); chest = getSign().getBlock(); } List list = null; if (lootTable != null) { list = lootTable.generateLootList(); } if (chestContent != null) { if (list != null) { list.addAll(Arrays.asList(chestContent)); } else { list = Arrays.asList(chestContent); } } if (list == null) { return; } chestContent = Arrays.copyOfRange(list.toArray(ItemStack[]::new), 0, 26); ContainerAdapter.getBlockInventory(chest).setContents(chestContent); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/FlagSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.TeamFlag; import de.erethon.xlib.util.NumberUtil; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class FlagSign extends Passive { private int team; public FlagSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Flag"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".flag"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return NumberUtil.parseInt(getLine(1), -1) != -1; } @Override public void initialize() { this.team = NumberUtil.parseInt(getLine(1)); if (getGame().getGroups().size() > team) { ((DGameWorld) getGameWorld()).addGameBlock(new TeamFlag(api, getSign().getBlock(), (DGroup) getGame().getGroups().get(team))); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/HologramSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import com.gmail.filoghost.holographicdisplays.api.Hologram; import com.gmail.filoghost.holographicdisplays.api.HologramsAPI; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.block.Sign; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class HologramSign extends Passive { private Hologram hologram; public HologramSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Hologram"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".hologram"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { if (Bukkit.getPluginManager().getPlugin("HolographicDisplays") == null) { markAsErroneous("HolographicDisplays not enabled"); return false; } return true; } @Override public void initialize() { String text = getGameWorld().getDungeon().getRules().getState(GameRule.MESSAGES).get(NumberUtil.parseInt(getLine(1))); if (text == null) { markAsErroneous("Unknown message, ID: " + getLine(1)); return; } String[] holoLines = text.split("(?i)
"); Location location = getSign().getLocation(); location = location.add(0.5, NumberUtil.parseDouble(getLine(2), 2.0), 0.5); hologram = HologramsAPI.createHologram(api, location); for (String line : holoLines) { if (line.startsWith("Item:")) { String id = line.replace("Item:", ""); ItemStack item = null; ExItem exItem = api.getXLib().getExItem(id); if (exItem != null) { item = exItem.toItemStack(); } hologram.appendItemLine(item); } else { hologram.appendTextLine(ChatColor.translateAlternateColorCodes('&', line)); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/InteractSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.util.NumberUtil; import java.util.HashSet; import java.util.Set; import org.bukkit.ChatColor; import org.bukkit.block.Sign; import org.bukkit.scheduler.BukkitRunnable; /** * @author Milan Albrecht, Daniel Saukel */ public class InteractSign extends Passive { private int id = 0; public InteractSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Interact"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".interact"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean isTriggerLineDisabled() { return true; } @Override public boolean validate() { Set used = new HashSet<>(); for (DungeonSign dSign : getEditWorld().getDungeonSigns()) { if (dSign instanceof InteractSign) { used.add(((InteractSign) dSign).id); } } if (getLine(1).isEmpty()) { if (!used.isEmpty()) { while (used.contains(id)) { id++; } } } else { id = NumberUtil.parseInt(getLine(1)); return !(id == 0 || used.contains(id)); } new BukkitRunnable() { @Override public void run() { getSign().setLine(1, String.valueOf(id)); getSign().update(true); } }.runTaskLater(api, 1L); return true; } @Override public void initialize() { getGameWorld().createTrigger(this, LogicalExpression.parse("I" + id)); getSign().setLine(0, ChatColor.DARK_BLUE + "############"); getSign().setLine(1, ChatColor.GREEN + getLine(2)); getSign().setLine(2, ChatColor.GREEN + getLine(3)); getSign().setLine(3, ChatColor.DARK_BLUE + "############"); getSign().update(); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/LobbySign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.sign.LocationSign; import org.bukkit.Location; import org.bukkit.block.Sign; /** * @author Frank Baumann, Daniel Saukel */ public class LobbySign extends Passive implements LocationSign { private Location targetLocation; public LobbySign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public Location getTargetLocation() { return targetLocation; } @Override public void setTargetLocation(Location location) { this.targetLocation = location; } @Override public String getName() { return "Lobby"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".lobby"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { LocationSign.super.initialize(); getGameWorld().setLobbyLocation(targetLocation); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/NoteSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class NoteSign extends Passive { public NoteSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Interact"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".note"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/PlaceSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.PlaceableBlock; import org.bukkit.block.Sign; /** * @author Frank Baumann, Daniel Saukel */ public class PlaceSign extends Passive { public PlaceSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Place"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".place"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { ((DGameWorld) getGameWorld()).addGameBlock(new PlaceableBlock(api, (DGameWorld) getGameWorld(), getSign().getBlock(), getLine(1), getLine(2))); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/ProtectionSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.ProtectedBlock; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class ProtectionSign extends Passive { public ProtectionSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Protection"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".protection"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { ((DGameWorld) getGameWorld()).addGameBlock(new ProtectedBlock(api, BlockUtilCompat.getAttachedBlock(getSign().getBlock()))); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/RewardChestSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.RewardChest; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.util.NumberUtil; import java.util.Arrays; import java.util.List; import org.bukkit.block.Sign; import org.bukkit.inventory.ItemStack; /** * @author Daniel Saukel */ public class RewardChestSign extends ChestSign { private double moneyReward; private int levelReward; public RewardChestSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public double getMoneyReward() { return moneyReward; } public void setMoneyReward(double amount) { moneyReward = amount; } public int getLevelReward() { return levelReward; } public void setLevelReward(int amount) { levelReward = amount; } @Override public String getName() { return "RewardChest"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".rewardchest"; } @Override public void initialize() { if (!getLine(1).isEmpty()) { String[] attributes = getLine(1).split(","); if (attributes.length >= 1) { moneyReward = NumberUtil.parseDouble(attributes[0]); } if (attributes.length >= 2) { levelReward = NumberUtil.parseInt(attributes[1]); } } if (!getLine(2).isEmpty()) { lootTable = api.getXLib().getLootTable(getLine(2)); } checkChest(); if (chest != null) { setToAir(); } else { getSign().getBlock().setType(VanillaItem.CHEST.getMaterial()); chest = getSign().getBlock(); } List list = null; if (lootTable != null) { list = lootTable.generateLootList(); } if (chestContent != null) { if (list != null) { list.addAll(Arrays.asList(chestContent)); } else { list = Arrays.asList(chestContent); } } if (list == null) { return; } ((DGameWorld) getGameWorld()).addGameBlock(new RewardChest((DungeonsXL) api, chest, moneyReward, levelReward, list.toArray(ItemStack[]::new))); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/ScriptSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class ScriptSign extends Passive { private String scriptName; public ScriptSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); scriptName = lines[1]; } public String getScriptName() { return scriptName; } public void setScriptName(String name) { scriptName = name; } @Override public String getName() { return "Script"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".script"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return ((DungeonsXL) api).getSignScriptRegistry().get(scriptName) != null; } @Override public void initialize() { SignScript script = ((DungeonsXL) api).getSignScriptRegistry().get(scriptName); if (script == null) { markAsErroneous("The script \"" + scriptName + "\" could not be found."); return; } DungeonSign dSign = null; for (String[] lines : script.getSigns()) { dSign = getGameWorld().createDungeonSign(getSign(), lines); if (dSign.isErroneous()) { getGameWorld().removeDungeonSign(dSign); continue; } try { dSign.initialize(); } catch (Exception exception) { dSign.markAsErroneous("An error occurred while initializing a sign of the type " + dSign.getName() + ". This is not a user error. Please report the following stacktrace to the developer of the plugin:"); exception.printStackTrace(); } if (!dSign.hasTriggers()) { try { dSign.trigger(null); } catch (Exception exception) { markAsErroneous("An error occurred while triggering a sign of the type " + getName() + ". This is not a user error. Please report the following stacktrace to the developer of the plugin:"); exception.printStackTrace(); } } } if (dSign == null) { markAsErroneous("The script \"" + scriptName + "\" could not be found."); return; } if (dSign.isSetToAir()) { dSign.setToAir(); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/SignScript.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.xlib.chat.MessageUtil; import java.io.File; import java.util.ArrayList; import java.util.List; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; /** * Representation of a sign script. Sign scripts allow to merge multiple dungeon signs at one position. * * @author Daniel Saukel */ public class SignScript { private String name; private List signs; /** * @param file the script file */ public SignScript(File file) { this(file.getName().substring(0, file.getName().length() - 4), YamlConfiguration.loadConfiguration(file)); } /** * @param name the name of the Announcer * @param config the config that stores the information */ public SignScript(String name, FileConfiguration config) { this.name = name; signs = new ArrayList<>(config.getKeys(false).size()); int i = 0; for (String key : config.getKeys(false)) { List lines = config.getStringList(key); if (lines.size() != 4) { MessageUtil.log("Found an invalid sign (ID: " + key + ") in script \"" + name + "\". Every sign must have 4 text lines."); continue; } signs.add(i, lines.toArray(new String[4])); i++; } } /** * @return the name of the announcer */ public String getName() { return name; } /** * @return the signs */ public List getSigns() { return signs; } /** * @param index the index number * @return the lines of the sign */ public String[] getLines(int index) { return signs.get(index); } /** * @param index the index number * @param lines the lines to set */ public void setLines(int index, String[] lines) { signs.set(index, lines); } @Override public String toString() { return getClass().getSimpleName() + "{name=" + name + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/passive/StartSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.passive; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Passive; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.sign.LocationSign; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Location; import org.bukkit.block.Sign; /** * @author Frank Baumann, Daniel Saukel */ public class StartSign extends Passive implements LocationSign { private Location targetLocation; private int id; public StartSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public Location getTargetLocation() { return targetLocation; } @Override public void setTargetLocation(Location location) { this.targetLocation = location; } @Override public String getName() { return "Start"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".start"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { LocationSign.super.initialize(); id = NumberUtil.parseInt(getLine(1)); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/rocker/BlockSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.rocker; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Rocker; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.compatibility.RuntimeTrait; import de.erethon.xlib.compatibility.RuntimeType; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.util.NumberUtil; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.bukkit.block.Block; import org.bukkit.block.Sign; /** * @author Milan Albrecht, Daniel Saukel */ public class BlockSign extends Rocker { private ExItem offBlock = VanillaItem.AIR; private ExItem onBlock = VanillaItem.AIR; private byte offBlockData = Byte.MIN_VALUE; private byte onBlockData = Byte.MIN_VALUE; public BlockSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Block"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".block"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return true; } @Override public boolean isSetToAir() { return false; } @Override public boolean validate() { return true; } @Override public void initialize() { if (getLine(1).isEmpty()) { offBlock = VanillaItem.AIR; } else { String[] line1 = getLine(1).split(","); offBlock = api.getXLib().getExItem(line1[0]); if (offBlock == null) { markAsErroneous("Could not recognize offBlock, input: " + getLine(1)); return; } if (line1.length > 1) { offBlockData = (byte) NumberUtil.parseInt(line1[1]); } } if (getLine(2).isEmpty()) { onBlock = VanillaItem.AIR; } else { String[] line2 = getLine(2).split(","); onBlock = api.getXLib().getExItem(line2[0]); if (onBlock == null) { markAsErroneous("Could not recognize onBlock, input: " + getLine(2)); return; } if (line2.length > 1) { onBlockData = (byte) NumberUtil.parseInt(line2[1]); } } getSign().getBlock().setType(offBlock.getMaterial()); try { setBlockData(getSign().getBlock(), offBlockData); } catch (IllegalArgumentException exception) { markAsErroneous("offBlock data value " + offBlockData + " cannot be applied to given type " + offBlock.getId()); } } @Override public void activate() { getSign().getBlock().setType(onBlock.getMaterial()); try { setBlockData(getSign().getBlock(), onBlockData); } catch (IllegalArgumentException exception) { markAsErroneous("onBlock data value " + onBlockData + " cannot be applied to given type " + onBlock.getId()); return; } active = true; } @Override public void deactivate() { getSign().getBlock().setType(offBlock.getMaterial()); try { setBlockData(getSign().getBlock(), offBlockData); } catch (IllegalArgumentException exception) { markAsErroneous("onBlock data value " + offBlockData + " cannot be applied to given type " + onBlock.getId()); return; } active = false; } private static Method craftBlockSetData; private static void setBlockData(Block block, byte data) { if (data == Byte.MIN_VALUE) { return; } if (craftBlockSetData == null) { try { String relocationTarget = RuntimeType.get().hasTrait(RuntimeTrait.OBC_RELOCATIONS) ? (Version.get().getRelocationTarget() + ".") : ""; craftBlockSetData = Class.forName( "org.bukkit.craftbukkit." + relocationTarget + "block.CraftBlock") .getDeclaredMethod("setData", byte.class); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | IllegalArgumentException exception) { exception.printStackTrace(); } } try { craftBlockSetData.invoke(block, data); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { exception.printStackTrace(); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/rocker/OpenDoorSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.rocker; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Rocker; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.dungeonsxl.world.block.LockedDoor; import de.erethon.xlib.category.Category; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public class OpenDoorSign extends Rocker { private LockedDoor door; public OpenDoorSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public LockedDoor getDoor() { return door; } public void setDoor(LockedDoor door) { this.door = door; } @Override public String getName() { return "Door"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".door"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { Block block = BlockUtilCompat.getAttachedBlock(getSign().getBlock()); if (Category.DOORS.containsBlock(block) || Category.FENCE_GATES.containsBlock(block) || Category.TRAPDOORS.containsBlock(block)) { if (block.getRelative(BlockFace.DOWN).getType() == block.getType()) { door = new LockedDoor(api, block.getRelative(BlockFace.DOWN)); } else { door = new LockedDoor(api, block); } ((DGameWorld) getGameWorld()).addGameBlock(door); } else { markAsErroneous("No door attached"); } } @Override public void activate() { if (door != null) { door.open(); active = true; } } @Override public void deactivate() { if (door != null) { door.close(); active = false; } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/rocker/TriggerSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.rocker; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.sign.Rocker; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.SignTrigger; import de.erethon.xlib.util.NumberUtil; import java.util.HashSet; import java.util.Set; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class TriggerSign extends Rocker { private int id = 0; public TriggerSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } @Override public String getName() { return "Trigger"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".trigger"; } @Override public boolean isOnDungeonInit() { return true; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { Set used = new HashSet<>(); for (DungeonSign dSign : getEditWorld().getDungeonSigns()) { if (dSign instanceof TriggerSign) { used.add(((TriggerSign) dSign).id); } } if (getLine(1).isEmpty()) { if (!used.isEmpty()) { while (used.contains(id)) { id++; } } } else { id = NumberUtil.parseInt(getLine(1)); return !(id == 0 || used.contains(id)); } new BukkitRunnable() { @Override public void run() { getSign().setLine(1, String.valueOf(id)); getSign().update(true); } }.runTaskLater(api, 1L); return true; } @Override public void initialize() { id = NumberUtil.parseInt(getLine(1), 1); } @Override public void activate() { SignTrigger trigger = SignTrigger.getById(id, getGameWorld()); if (trigger != null) { trigger.trigger(true, null); } } @Override public boolean activate(Player player) { SignTrigger trigger = SignTrigger.getById(id, getGameWorld()); if (trigger == null) { return false; } trigger.trigger(true, player); return true; } @Override public void deactivate() { SignTrigger trigger = SignTrigger.getById(id, getGameWorld()); if (trigger != null) { trigger.trigger(false, null); } } @Override public boolean deactivate(Player player) { SignTrigger trigger = SignTrigger.getById(id, getGameWorld()); if (trigger == null) { return false; } trigger.trigger(false, player); return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/CommandScript.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import de.erethon.dungeonsxl.DungeonsXL; import java.io.File; import java.util.List; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.permissions.Permission; /** * @author Daniel Saukel */ public class CommandScript { private String name; private File file; private List commands; public CommandScript(String name, List commands, Permission permission) { this.name = name; file = new File(DungeonsXL.COMMANDS, name + ".yml"); setCommands(commands); } public CommandScript(File file) { FileConfiguration config = YamlConfiguration.loadConfiguration(file); name = file.getName().replace(".yml", ""); this.file = file; setCommands(config.getStringList("commands")); } public String getName() { return name; } public File getFile() { return file; } public List getCommands() { return commands; } public void setCommands(List commands) { this.commands = commands; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/CommandSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Windup; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.dungeonsxl.trigger.InteractTrigger; import de.erethon.xlib.util.EnumUtil; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Bukkit; import org.bukkit.block.Sign; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class CommandSign extends Windup { public enum Executor { DEFAULT, OP, CONSOLE } private CommandScript script; private Executor executor; public CommandSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public CommandScript getScript() { return script; } @Override public String getName() { return "CMD"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".cmd"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } public Executor getExecutor() { return executor; } @Override public boolean validate() { script = ((DungeonsXL) api).getCommandScriptRegistry().get(getLine(1)); return script != null && !script.getCommands().isEmpty(); } @Override public void initialize() { script = ((DungeonsXL) api).getCommandScriptRegistry().get(getLine(1)); String[] attributes = getLine(2).split(","); if (attributes.length == 3) { delay = NumberUtil.parseDouble(attributes[0]); interval = NumberUtil.parseDouble(attributes[1]); executor = EnumUtil.getEnumIgnoreCase(Executor.class, attributes[2]); if (executor == null) { executor = Executor.DEFAULT; } } else if (attributes.length == 2) { delay = NumberUtil.parseDouble(attributes[0]); interval = NumberUtil.parseDouble(attributes[1], -1); if (interval == -1) { interval = delay; executor = EnumUtil.getEnumIgnoreCase(Executor.class, attributes[1]); } if (executor == null) { executor = Executor.DEFAULT; } } else if (attributes.length == 1) { delay = NumberUtil.parseDouble(attributes[0], -1); if (delay == -1) { delay = 0; interval = 0; executor = EnumUtil.getEnumIgnoreCase(Executor.class, attributes[0]); } if (executor == null) { executor = Executor.DEFAULT; } } else if (attributes.length == 0) { executor = Executor.DEFAULT; } n = script.getCommands().size(); setRunnable(new CommandTask(this, Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI"))); if (!getTriggers().isEmpty()) { setToAir(); return; } InteractTrigger.addDefault(api, this, script.getName(), ""); } @Override public void activate() { if (executor == Executor.CONSOLE) { ((CommandTask) getRunnable()).setSender(Bukkit.getConsoleSender(), false); startTask(); active = true; } else { markAsErroneous("Sign is set to be performed by a player but is triggered by a trigger that cannot be attributed to a player (e.g. mob)"); } } @Override public boolean activate(Player player) { CommandSender sender = player; boolean wasOp = player.isOp(); if (executor == Executor.CONSOLE) { sender = Bukkit.getConsoleSender(); } ((CommandTask) getRunnable()).setSender(sender, wasOp); ((CommandTask) getRunnable()).setPlayer(player); startTask(); active = true; return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/CommandTask.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Daniel Saukel */ public class CommandTask extends BukkitRunnable { private Player player; private boolean wasOp; private CommandSign sign; private CommandScript script; private CommandSender sender; private boolean papi; private int k; public CommandTask(CommandSign sign, boolean papi) { this.sign = sign; this.script = sign.getScript(); this.papi = papi; } public void setSender(CommandSender sender, boolean wasOp) { this.sender = sender; this.wasOp = wasOp; } public void setPlayer(Player player) { this.player = player; } @Override public void run() { if (sign.isWorldFinished()) { sign.deactivate(); return; } if (k >= script.getCommands().size()) { sign.deactivate(); k = 0; return; } String command = script.getCommands().get(k++) .replace("%world%", sign.getGameWorld().getWorld().getName()).replace("%world_name%", sign.getGameWorld().getWorld().getName()); if (player != null) { command = command.replace("%player%", player.getName()).replace("%player_name%", player.getName()); } if (sign.getExecutor() == CommandSign.Executor.OP) { sender.setOp(true); } if (papi) { Bukkit.getServer().dispatchCommand(sender, PlaceholderAPI.setPlaceholders(player, command)); } else { Bukkit.getServer().dispatchCommand(sender, command); } if (sign.getExecutor() == CommandSign.Executor.OP) { sender.setOp(wasOp); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/DelayedPowerTask.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import org.bukkit.Bukkit; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class DelayedPowerTask extends BukkitRunnable { private String worldName; private RedstoneSign sign; private boolean enable; public DelayedPowerTask(RedstoneSign sign, boolean enable) { worldName = sign.getSign().getWorld().getName(); this.sign = sign; this.enable = enable; } @Override public void run() { if (Bukkit.getWorld(worldName) == null) { sign.getEnableTask().cancel(); sign.getDisableTask().cancel(); return; } if (enable) { sign.power(); if (sign.getRepeatsToDo() == 1) { sign.getEnableTask().cancel(); } } else { sign.unpower(); if (sign.getRepeatsToDo() == 1) { sign.getDisableTask().cancel(); } sign.setRepeatsToDo(sign.getRepeatsToDo() - 1); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/DropSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Windup; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Location; import org.bukkit.block.Sign; import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class DropSign extends Windup { private ItemStack item; private Location spawnLocation; public DropSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public ItemStack getItem() { return item; } public void setItem(ItemStack item) { this.item = item; } @Override public String getName() { return "Drop"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".drop"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return api.getXLib().getExItem(getLine(1)) != null; } @Override public void initialize() { ExItem item = api.getXLib().getExItem(getLine(1)); String[] attributes = getLine(2).split(","); if (attributes.length >= 1) { this.item = item.toItemStack(NumberUtil.parseInt(attributes[0], 1)); } if (attributes.length == 2) { interval = NumberUtil.parseDouble(attributes[1]); } spawnLocation = getSign().getLocation().add(0.5, 0, 0.5); setRunnable(new BukkitRunnable() { @Override public void run() { if (isWorldFinished()) { deactivate(); return; } try { spawnLocation.getWorld().dropItem(spawnLocation, getItem()); } catch (NullPointerException exception) { deactivate(); } } }); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/MobSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.mob.ExternalMobProvider; import de.erethon.dungeonsxl.api.sign.Windup; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.mob.ExMob; import de.erethon.xlib.util.NumberUtil; import de.erethon.xlib.util.Registry; import java.util.ArrayList; import java.util.Collection; import org.bukkit.Location; import org.bukkit.block.Sign; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class MobSign extends Windup { private Registry providers; private String mob; private ExternalMobProvider provider; private Collection spawnedMobs = new ArrayList<>(); private int initialAmount; public MobSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); providers = api.getExternalMobProviderRegistry(); } public String getMob() { return mob; } public void setMob(String mob) { this.mob = mob; } /** * Returns the initial amount of mobs to spawn - this value may increase with waves. * * @return the initial amount of mobs to spawn - this value may increase with waves */ public int getInitialAmount() { return initialAmount; } public Collection getSpawnedMobs() { return spawnedMobs; } @Override public String getName() { return "Mob"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".mob"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { if (getLine(1).isEmpty() || getLine(2).isEmpty()) { return false; } String[] attributes = getLine(2).split(","); return attributes.length == 2 || attributes.length == 3; } @Override public void initialize() { mob = getLine(1); String[] attributes = getLine(2).split(","); interval = NumberUtil.parseDouble(attributes[0]); n = NumberUtil.parseInt(attributes[1]); initialAmount = n; provider = attributes.length == 3 ? providers.get(attributes[2]) : null; setRunnable(new MobSpawnTask(api, this, n)); } /** * Spawns the mob. * * @return the spawned mob */ public LivingEntity spawn() { Location spawnLoc = getSign().getLocation().add(0.5, 0, 0.5); LivingEntity spawned = null; if (provider == null) { ExMob type = api.getXLib().getExMob(mob); if (type == null || !type.getSpecies().isAlive()) { return null; } spawned = (LivingEntity) type.toEntity(spawnLoc); } else { provider.summon(mob, spawnLoc); for (Entity entity : spawnLoc.getChunk().getEntities()) { Location entityLoc = entity.getLocation(); if (entityLoc.getX() >= spawnLoc.getX() - 1 && entityLoc.getX() <= spawnLoc.getX() + 1 && entityLoc.getY() >= spawnLoc.getY() - 1 && entityLoc.getY() <= spawnLoc.getY() + 1 && entityLoc.getZ() >= spawnLoc.getZ() - 1 && entityLoc.getZ() <= spawnLoc.getZ() + 1 && entity instanceof LivingEntity && !spawnedMobs.contains((LivingEntity) entity) && !(entity instanceof Player)) { spawned = (LivingEntity) entity; } } } if (spawned == null) { return null; } spawned.setRemoveWhenFarAway(false); spawnedMobs.add(spawned); return spawned; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/MobSpawnTask.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import de.erethon.dungeonsxl.api.DungeonsAPI; import org.bukkit.entity.LivingEntity; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Daniel Saukel */ public class MobSpawnTask extends BukkitRunnable { private DungeonsAPI api; private MobSign sign; private int k = 1, n; public MobSpawnTask(DungeonsAPI api, MobSign sign, int n) { this.api = api; this.sign = sign; this.n = n; } @Override public void run() { if (sign.isWorldFinished()) { sign.deactivate(); return; } LivingEntity entity = sign.spawn(); if (entity != null) { api.wrapEntity(entity, sign.getGameWorld(), api.getXLib().getExMob(entity), sign.getMob()); } if (k < n) { k++; } else { sign.deactivate(); k = 1; } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/sign/windup/RedstoneSign.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.sign.windup; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Rocker; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPermission; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.util.NumberUtil; import org.bukkit.block.Sign; import org.bukkit.scheduler.BukkitTask; /** * @author Frank Baumann, Daniel Saukel */ public class RedstoneSign extends Rocker { private BukkitTask enableTask; private BukkitTask disableTask; private long delay = 0; private long offDelay = 0; private int repeat = 1; private int repeatsToDo = 1; public RedstoneSign(DungeonsAPI api, Sign sign, String[] lines, InstanceWorld instance) { super(api, sign, lines, instance); } public BukkitTask getEnableTask() { return enableTask; } public void setEnableTask(BukkitTask enableTask) { this.enableTask = enableTask; } public BukkitTask getDisableTask() { return disableTask; } public void setDisableTask(BukkitTask disableTask) { this.disableTask = disableTask; } public long getDelay() { return delay; } public void setDelay(long delay) { this.delay = delay; } public long getOffDelay() { return offDelay; } public void setOffDelay(long offDelay) { this.offDelay = offDelay; } public int getRepeat() { return repeat; } public void setRepeat(int repeat) { this.repeat = repeat; } public int getRepeatsToDo() { return repeatsToDo; } public void setRepeatsToDo(int repeatsToDo) { this.repeatsToDo = repeatsToDo; } @Override public String getName() { return "Redstone"; } @Override public String getBuildPermission() { return DPermission.SIGN.getNode() + ".redstone"; } @Override public boolean isOnDungeonInit() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isSetToAir() { return true; } @Override public boolean validate() { return true; } @Override public void initialize() { int line1 = 0; int line11 = 0; if (!getLine(1).isEmpty()) { String line[] = getLine(1).split(","); line1 = NumberUtil.parseInt(line[0]); if (line.length > 1) { line11 = NumberUtil.parseInt(line[1]); } } int line2 = 1; if (!getLine(2).isEmpty()) { line2 = NumberUtil.parseInt(getLine(2)); } if (line1 > 0) { delay = (long) line1 * 2; if (line11 > 0) { offDelay = (long) line11 * 2; } else { offDelay = delay; } if (line2 >= 0) { repeat = line2; } } } @Override public void activate() { if (active) { return; } if (delay > 0) { enableTask = new DelayedPowerTask(this, true).runTaskTimer(api, delay, delay + offDelay); if (repeat != 1) { repeatsToDo = repeat; disableTask = new DelayedPowerTask(this, false).runTaskTimer(api, delay + offDelay, delay + offDelay); } } else { power(); } active = true; } @Override public void deactivate() { if (!active) { return; } unpower(); if (enableTask != null) { enableTask.cancel(); } if (disableTask != null) { disableTask.cancel(); } active = false; } public void power() { getSign().getBlock().setType(VanillaItem.REDSTONE_BLOCK.getMaterial()); } public void unpower() { setToAir(); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/DistanceTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.xlib.util.NumberUtil; import org.bukkit.Location; import org.bukkit.entity.Player; /** * @author Frank Baumann, Daniel Saukel */ public class DistanceTrigger extends AbstractTrigger { private int distance = 5; private Location loc; public DistanceTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); distance = NumberUtil.parseInt(value, distance); if (distance < 2) { distance = 2; } this.loc = owner.getLocation(); } @Override public char getKey() { return TriggerTypeKey.DISTANCE; } @Override public void onTrigger(boolean switching) { setTriggered(true); unregisterTrigger(); getListeners().clear(); getGameWorld().unregisterTrigger(this); } /* Statics */ public static void triggerAllInDistance(Player player, DGameWorld gameWorld) { if (!player.getLocation().getWorld().equals(gameWorld.getWorld())) { return; } for (Trigger trigger : gameWorld.getTriggers().toArray(Trigger[]::new)) { if (!(trigger instanceof DistanceTrigger)) { continue; } DistanceTrigger distanceTrigger = (DistanceTrigger) trigger; if (player.getLocation().distance(distanceTrigger.loc) < distanceTrigger.distance) { distanceTrigger.trigger(true, player); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/FortuneTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.xlib.util.NumberUtil; import java.util.Random; /** * @author Daniel Saukel */ public class FortuneTrigger extends AbstractTrigger { private double chance = 0; public FortuneTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); this.chance = NumberUtil.parseDouble(value, chance); } @Override public char getKey() { return TriggerTypeKey.FORTUNE; } /* Getters and setters */ /** * @return the chance */ public double getChance() { return chance; } /** * @param chance the chance to set */ public void setChance(double chance) { this.chance = chance; } /* Actions */ @Override public void onTrigger(boolean switching) { int random = new Random().nextInt(100); if (chance * 100 >= random) { setTriggered(true); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/InteractTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.util.NumberUtil; import org.bukkit.ChatColor; import org.bukkit.block.Block; import org.bukkit.block.Sign; /** * @author Frank Baumann, Daniel Saukel */ public class InteractTrigger extends AbstractTrigger { private static int unusedId = Integer.MIN_VALUE; private int id; private Block interactBlock; public InteractTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); id = NumberUtil.parseInt(value); interactBlock = getGameWorld().getWorld().getBlockAt(owner.getLocation()); } private InteractTrigger(DungeonsAPI api, TriggerListener owner) { super(api, owner, LogicalExpression.parse("I" + unusedId), String.valueOf(unusedId++)); interactBlock = getGameWorld().getWorld().getBlockAt(owner.getLocation()); } public Block getInteractBlock() { return interactBlock; } public void setInteractBlock(Block block) { interactBlock = block; } @Override public char getKey() { return TriggerTypeKey.INTERACT; } @Override public void onTrigger(boolean switching) { setTriggered(true); } /* Statics */ public static InteractTrigger getByBlock(Block block, GameWorld gameWorld) { if (block == null || gameWorld == null) { return null; } for (Trigger uncasted : gameWorld.getTriggers()) { if (!(uncasted instanceof InteractTrigger)) { continue; } InteractTrigger trigger = (InteractTrigger) uncasted; if (block.equals(trigger.interactBlock)) { return trigger; } } return null; } public static InteractTrigger getById(int id, GameWorld gameWorld) { if (gameWorld == null) { return null; } for (Trigger uncasted : gameWorld.getTriggers()) { if (!(uncasted instanceof InteractTrigger)) { continue; } InteractTrigger trigger = (InteractTrigger) uncasted; if (id == trigger.id) { return trigger; } } return null; } public static void addDefault(DungeonsAPI api, DungeonSign dungeonSign, String line1, String line2) { InteractTrigger trigger = new InteractTrigger(api, dungeonSign); trigger.addListener(dungeonSign); Sign sign = dungeonSign.getSign(); sign.setLine(0, ChatColor.DARK_BLUE + "############"); sign.setLine(1, ChatColor.GREEN + line1); sign.setLine(2, ChatColor.GREEN + line2); sign.setLine(3, ChatColor.DARK_BLUE + "############"); sign.update(); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/MobTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; /** * @author Frank Baumann, Daniel Saukel */ public class MobTrigger extends AbstractTrigger { private String name; public MobTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); name = value; } @Override public char getKey() { return TriggerTypeKey.MOB; } @Override public void onTrigger(boolean switching) { setTriggered(true); } /* Statics */ public static MobTrigger getByName(String name, GameWorld gameWorld) { if (name == null || gameWorld == null) { return null; } for (Trigger uncasted : gameWorld.getTriggers()) { if (!(uncasted instanceof MobTrigger)) { continue; } MobTrigger trigger = (MobTrigger) uncasted; if (name.equalsIgnoreCase(trigger.name)) { return trigger; } } return null; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/PresenceTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; /** * @author Daniel Saukel */ public class PresenceTrigger extends DistanceTrigger { public PresenceTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); } @Override public char getKey() { return TriggerTypeKey.PRESENCE; } @Override public void onTrigger(boolean switching) { setTriggered(true); unregisterTrigger(); getListeners().clear(); getGameWorld().unregisterTrigger(this); } @Override public void postTrigger() { setTriggered(false); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/ProgressTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.world.DResourceWorld; import de.erethon.xlib.util.NumberUtil; /** * @author Frank Baumann, Daniel Saukel */ public class ProgressTrigger extends AbstractTrigger { private DResourceWorld floor; private int floorCount; private int waveCount; // Unused public ProgressTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { this(api, owner, expression, value, NumberUtil.parseInt(value.split("/")[0]), NumberUtil.parseInt(value.split("/")[1])); } public ProgressTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value, int floorCount, int waveCount) { super(api, owner, expression, value); this.floorCount = floorCount; this.waveCount = waveCount; } @Override public char getKey() { return TriggerTypeKey.PROGRESS; } /* Getters and setters */ /** * @return the specific floor that must be finished */ public DResourceWorld getFloor() { return floor; } /** * @param floor the specific floor to set */ public void setFloor(DResourceWorld floor) { this.floor = floor; } /** * @return the floor count to trigger */ public int getFloorCount() { return floorCount; } /** * @param floorCount the floor count to set */ public void setFloorCount(int floorCount) { this.floorCount = floorCount; } /** * @return the wave count to trigger */ public int getWaveCount() { return waveCount; } /** * @param waveCount the wave count to set */ public void setWaveCount(int waveCount) { this.waveCount = waveCount; } /* Actions */ @Override public void onTrigger(boolean switching) { setTriggered(true); } /* Statics */ public static ProgressTrigger getOrCreate(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { String[] values = value.split("/"); int floorCount = NumberUtil.parseInt(values[0]); int waveCount = NumberUtil.parseInt(values[1]); if (floorCount == 0 & waveCount == 0 || floorCount < 0 || waveCount < 0) { return null; } return new ProgressTrigger(api, owner, expression, value, floorCount, waveCount); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/RedstoneTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.sign.Deactivatable; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.xlib.category.Category; import org.bukkit.Location; import org.bukkit.block.Block; /** * @author Frank Baumann, Daniel Saukel */ public class RedstoneTrigger extends AbstractTrigger { private Block rtBlock; public RedstoneTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); Location loc = owner.getLocation(); if (Category.WALL_SIGNS.containsBlock(loc.getBlock())) { rtBlock = BlockUtilCompat.getAttachedBlock(loc.getBlock()); } else { rtBlock = loc.getBlock(); } } @Override public char getKey() { return TriggerTypeKey.REDSTONE; } @Override public void onTrigger(boolean switching) { if (rtBlock.isBlockPowered()) { if (!isTriggered()) { setTriggered(true); } } else if (isTriggered()) { setTriggered(false); for (TriggerListener listener : getListeners().toArray(TriggerListener[]::new)) { if (!(listener instanceof Deactivatable)) { return; } Deactivatable sign = ((Deactivatable) listener); if (sign.isErroneous()) { return; } for (Trigger trigger : sign.getTriggers()) { if (trigger.isTriggered()) { return; } } sign.deactivate(); } } } /* Statics */ public static void updateAll(GameWorld gameWorld) { if (gameWorld == null) { return; } for (Trigger uncasted : gameWorld.getTriggers()) { if (!(uncasted instanceof RedstoneTrigger)) { continue; } ((RedstoneTrigger) uncasted).trigger(true, null); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/SignTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.util.NumberUtil; /** * @author Frank Baumann, Daniel Saukel */ public class SignTrigger extends AbstractTrigger { private int id; public SignTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); char key = expression.getText().charAt(0); int i = Character.toUpperCase(key) == getKey() ? 1 : 0; id = NumberUtil.parseInt(expression.getText().substring(i)); } @Override public char getKey() { return TriggerTypeKey.GENERIC; } @Override public void onTrigger(boolean switching) { if (switching != isTriggered()) { setTriggered(switching); } } /* Statics */ public static SignTrigger getById(int id, GameWorld gameWorld) { if (gameWorld == null) { return null; } for (Trigger uncasted : gameWorld.getTriggers()) { if (!(uncasted instanceof SignTrigger)) { continue; } SignTrigger trigger = (SignTrigger) uncasted; if (id == trigger.id) { return trigger; } } return null; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/TriggerListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.player.DPlayerListener; import de.erethon.xlib.item.VanillaItem; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockRedstoneEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.BookMeta; import org.bukkit.scheduler.BukkitRunnable; /** * @author Milan Albrecht, Daniel Saukel */ public class TriggerListener implements Listener { private DungeonsXL plugin; public TriggerListener(DungeonsXL plugin) { this.plugin = plugin; } @EventHandler public void onBlockRedstone(final BlockRedstoneEvent event) { new BukkitRunnable() { @Override public void run() { GameWorld gameWorld = plugin.getGameWorld(event.getBlock().getWorld()); if (gameWorld != null) { RedstoneTrigger.updateAll(gameWorld); } } }.runTaskLater(plugin, 1L); } @EventHandler public void onPlayerInteract(PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK && event.getAction() != Action.RIGHT_CLICK_AIR) { return; } Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player)) { return; } GameWorld gameWorld = plugin.getGameWorld(player.getWorld()); if (gameWorld == null) { return; } ItemStack item = event.getItem(); if (item == null) { return; } String name = null; if (item.hasItemMeta()) { if (item.getItemMeta().hasDisplayName()) { name = item.getItemMeta().getDisplayName(); } else if (VanillaItem.WRITTEN_BOOK.is(item) || VanillaItem.WRITABLE_BOOK.is(item)) { if (item.getItemMeta() instanceof BookMeta) { BookMeta meta = (BookMeta) item.getItemMeta(); if (meta.hasTitle()) { name = meta.getTitle(); } } } } if (name == null) { name = plugin.getXLib().getExItem(item).getName(); } UseItemTrigger trigger = UseItemTrigger.getByName(name, gameWorld); if (trigger != null) { trigger.trigger(true, player); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/UseItemTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.xlib.item.ExItem; /** * @author Frank Baumann, Daniel Saukel */ public class UseItemTrigger extends AbstractTrigger { private String name; private String matchedName; public UseItemTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); name = value; ExItem item = api.getXLib().getExItem(name); if (item != null) { matchedName = item.toString(); } } @Override public char getKey() { return TriggerTypeKey.USE_ITEM; } @Override public void onTrigger(boolean switching) { setTriggered(true); } /* Statics */ public static UseItemTrigger getByName(String name, GameWorld gameWorld) { if (name == null || gameWorld == null) { return null; } for (Trigger uncasted : gameWorld.getTriggers()) { if (!(uncasted instanceof UseItemTrigger)) { continue; } UseItemTrigger trigger = (UseItemTrigger) uncasted; if (name.equalsIgnoreCase(trigger.name)) { return trigger; } else if (name.equalsIgnoreCase(trigger.matchedName)) { return trigger; } } return null; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/trigger/WaveTrigger.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.trigger; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.trigger.AbstractTrigger; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.xlib.util.NumberUtil; /** * @author Frank Baumann, Daniel Saukel */ public class WaveTrigger extends AbstractTrigger { private double mustKillRate = 1; public WaveTrigger(DungeonsAPI api, TriggerListener owner, LogicalExpression expression, String value) { super(api, owner, expression, value); mustKillRate = NumberUtil.parseDouble(value, mustKillRate); } @Override public char getKey() { return TriggerTypeKey.WAVE; } /** * @return the minimal mob kill rate to trigger the wave */ public double getMustKillRate() { return mustKillRate; } /** * @param mustKillRate the minimal mob kill rate to trigger the wave to set */ public void setMustKillRate(double mustKillRate) { this.mustKillRate = mustKillRate; } @Override public void onTrigger(boolean switching) { setTriggered(true); } @Override public void postTrigger() { setTriggered(false); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/AttributeUtil.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import com.google.common.collect.ImmutableMap; import de.erethon.xlib.compatibility.Version; import java.util.Map; import org.bukkit.Registry; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.entity.Player; /** * @author Daniel Saukel */ public class AttributeUtil { public static final Attribute ATTACK_DAMAGE = Attribute.valueOf(Version.isAtLeast(Version.MC1_21_2) ? "ATTACK_DAMAGE" : "GENERIC_ATTACK_DAMAGE"); public static final Attribute MAX_HEALTH = Attribute.valueOf(Version.isAtLeast(Version.MC1_21_2) ? "MAX_HEALTH" : "GENERIC_MAX_HEALTH"); public static final Attribute MOVEMENT_SPEED = Attribute.valueOf(Version.isAtLeast(Version.MC1_21_2) ? "MOVEMENT_SPEED" : "GENERIC_MOVEMENT_SPEED"); private static final Map DEFAULT_PLAYER_VALUES = ImmutableMap.of( MOVEMENT_SPEED, .1, // .7 ATTACK_DAMAGE, 1.0 // 2.0 ); /** * Returns the attribute represented by the key. * * @param key the key; not null * @return the attribute represented by the key */ public static Attribute get(String key) { Attribute attribute; if (Version.isAtLeast(Version.MC1_21_2)) { attribute = Registry.ATTRIBUTE.match(key); if (attribute == null) { // Compatibility upon Minecraft updates attribute = Registry.ATTRIBUTE.match(key.replace("GENERIC_", "")); } } else { try { attribute = Attribute.valueOf(key); } catch (Exception exception) { attribute = null; } } return attribute; } /** * Returns the default value that a player entity has. * * @param attribute the attribute instance to check * @return the default value that a player entity has */ public static final Double getDefaultPlayerValue(AttributeInstance attribute) { return DEFAULT_PLAYER_VALUES.getOrDefault(attribute.getAttribute(), attribute.getDefaultValue()); } /** * Resets a player's attributes. * * @param player the player */ public static void resetPlayerAttributes(Player player) { for (Attribute attribute : Attribute.values()) { AttributeInstance instance = player.getAttribute(attribute); if (instance == null) { continue; } instance.setBaseValue(getDefaultPlayerValue(instance)); instance.getModifiers().forEach(instance::removeModifier); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/BlockUtilCompat.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import de.erethon.xlib.compatibility.Version; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.data.Directional; import org.bukkit.material.Attachable; import org.bukkit.material.MaterialData; /** * @author Daniel Saukel */ public class BlockUtilCompat { /** * Returns the block the given block is attached to. * * @param block the block to check * @return the attached block */ public static Block getAttachedBlock(Block block) { if (Version.isAtLeast(Version.MC1_13)) { if (block.getBlockData() instanceof Directional) { Directional data = (Directional) block.getBlockData(); if (data.getFaces().size() == 4) { return block.getRelative(data.getFacing().getOppositeFace()); } } return block.getRelative(BlockFace.DOWN); } else { MaterialData meta = block.getState().getData(); BlockFace blockFace = BlockFace.DOWN; if (meta instanceof Attachable) { blockFace = ((Attachable) meta).getAttachedFace(); } return block.getRelative(blockFace); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/ContainerAdapter.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import de.erethon.xlib.compatibility.Version; import org.bukkit.block.Block; import org.bukkit.block.Chest; import org.bukkit.block.Container; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.InventoryHolder; /** * @author Daniel Saukel */ public class ContainerAdapter { public static boolean isValidContainer(Block block) { if (Version.isAtLeast(Version.MC1_12_1)) { return block.getState() instanceof Container; } else { return block.getState() instanceof Chest; } } public static boolean isValidContainer(Inventory inventory) { if (Version.isAtLeast(Version.MC1_12_1)) { return inventory.getHolder() instanceof Container; } else { return inventory.getHolder() instanceof Chest; } } public static Block getHolderBlock(InventoryHolder holder) { if (Version.isAtLeast(Version.MC1_12_1)) { return ((Container) holder).getBlock(); } else { return ((Chest) holder).getBlock(); } } public static Inventory getBlockInventory(Block block) { if (Version.isAtLeast(Version.MC1_12_1)) { return ((Container) block.getState()).getInventory(); } else { return ((Chest) block.getState()).getInventory(); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/DependencyVersion.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.plugin.PluginMeta; import de.erethon.xlib.spiget.comparator.VersionComparator; import java.io.IOException; import java.util.Properties; import java.util.function.Predicate; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; /** * Lists compatible plugin versions. * * @author Daniel Saukel */ public enum DependencyVersion { XLIB("XLib-Runtime", getProperties().getProperty("dependencyVersion.xlib")), BOSSSHOP("BossShop", getProperties().getProperty("dependencyVersion.bossshop")), CITIZENS("Citizens", getProperties().getProperty("dependencyVersion.citizens")), HOLOGRAPHIC_DISPLAYS("HolographicDisplays", getProperties().getProperty("dependencyVersion.holographicdisplays")), MODERN_LWC("LWC", "2.1.5-09ad392"), PARTIES("Parties", getProperties().getProperty("dependencyVersion.parties")), PLACEHOLDER_API("PlaceholderAPI", getProperties().getProperty("dependencyVersion.placeholderapi")), VAULT("Vault", "1.7.3-b131"), // Two public plugins share this name CUSTOM_MOBS("CustomMobs", "4.17", s -> { try { Class.forName("de.hellfirepvp.CustomMobs"); return true; } catch (ClassNotFoundException e) { return false; } }), INSANE_MOBS("InsaneMobs2", "3.0.1"), MYTHIC_MOBS("MythicMobs", "5.11.2-6a371d59"); /** * Meta information about this project. */ public static final PluginMeta META = new PluginMeta.Builder("DungeonsXL") .minVersion(Version.MC1_8_8) .paperState(PluginMeta.State.NOT_SUPPORTED) .spigotState(PluginMeta.State.SUPPORTED) .economyState(PluginMeta.State.SUPPORTED) .permissionsState(PluginMeta.State.SUPPORTED) .spigotMCResourceId(9488) .bStatsResourceId(1039) .versionComparator(VersionComparator.SEM_VER_SNAPSHOT) .build(); private static Properties properties; private String name; private String version; private Plugin plugin; DependencyVersion(String name, String version) { this(name, version, null); } DependencyVersion(String name, String version, Predicate enabled) { this.name = name; this.version = version; if (enabled == null || enabled.test(name)) { plugin = Bukkit.getPluginManager().getPlugin(name); } } public String getName() { return name; } public String getSupportedVersion() { return version; } public String getEnabledVersion() { return plugin.getDescription().getVersion(); } public boolean isEnabled() { return plugin != null; } public boolean check() { return isEnabled() && getSupportedVersion().equals(getEnabledVersion()); } public static Properties getProperties() { if (properties == null) { properties = new Properties(); try { properties.load(DungeonsXL.class.getClassLoader().getResourceAsStream("dxl.properties")); } catch (IOException exception) { exception.printStackTrace(); } } return properties; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/LWCUtil.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import com.griefcraft.lwc.LWC; import com.griefcraft.model.Protection; import org.bukkit.Bukkit; import org.bukkit.block.Block; /** * @author Daniel Saukel */ public class LWCUtil { public static void removeProtection(Block block) { if (!isLWCLoaded()) { return; } Protection protection = LWC.getInstance().getProtectionCache().getProtection(block); if (protection != null) { protection.remove(); } } /** * @return true if LWC is loaded */ public static boolean isLWCLoaded() { return Bukkit.getPluginManager().getPlugin("LWC") != null; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/LocationString.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; /** * @author Daniel Saukel */ public class LocationString { private String raw; private String world; private double x, y, z; private float yaw, pitch; private Location location; private LocationString(String string) { raw = string; } public static LocationString fromString(String string) { if (string == null) { return null; } String[] args = string.split(","); if (args.length < 4 || args.length == 5 || args.length > 6) { return null; } double x, y, z; float yaw, pitch; try { x = Double.parseDouble(args[1]); y = Double.parseDouble(args[2]); z = Double.parseDouble(args[3]); if (args.length == 6) { yaw = Float.parseFloat(args[4]); pitch = Float.parseFloat(args[5]); } else { yaw = 0f; pitch = 0f; } } catch (NumberFormatException exception) { return null; } LocationString locationString = new LocationString(string); locationString.world = args[0]; locationString.x = x; locationString.y = y; locationString.z = z; locationString.yaw = yaw; locationString.pitch = pitch; return locationString; } public Location getLocation() { if (location == null) { World bukkitWorld = Bukkit.getWorld(world); if (bukkitWorld == null) { return null; } location = new Location(bukkitWorld, x, y, z, yaw, pitch); } return location; } @Override public String toString() { return raw; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/ParsingUtil.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import de.erethon.dungeonsxl.api.player.GlobalPlayer; import de.erethon.dungeonsxl.player.DGroup; /** * @author Daniel Saukel */ public enum ParsingUtil { GROUP_COLOR("%group_color%"), GROUP_NAME("%group_name%"), PLAYER_NAME("%player_name%"); private String placeholder; ParsingUtil(String placeholder) { this.placeholder = placeholder; } /* Getters and setters */ /** * @return the placeholder */ public String getPlaceholder() { return placeholder; } @Override public String toString() { return placeholder; } /* Statics */ /** * Replace the placeholders that are relevant for the chat in a String automatically. * * @param string the String that contains the placeholders * @param sender the GlobalPlayer who sent the message * @return the string with the placeholders replaced */ public static String replaceChatPlaceholders(String string, GlobalPlayer sender) { string = string.replaceAll(PLAYER_NAME.getPlaceholder(), sender.getName()); DGroup group = (DGroup) sender.getGroup(); if (group != null) { string = string.replaceAll(GROUP_COLOR.getPlaceholder(), group.getDColor().getChatColor().toString()); string = string.replaceAll(GROUP_NAME.getPlaceholder(), group.getName()); } return string; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/util/PlaceholderUtil.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.util; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.world.GameWorld; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.OfflinePlayer; /** * @author Daniel Saukel */ public class PlaceholderUtil extends PlaceholderExpansion { private DungeonsXL plugin; private String identifier; public PlaceholderUtil(DungeonsXL plugin, String identifier) { this.plugin = plugin; this.identifier = identifier; } @Override public String getAuthor() { return plugin.getDescription().getAuthors().toString(); } @Override public String getIdentifier() { return identifier; } @Override public String getRequiredPlugin() { return plugin.getName(); } @Override public String getVersion() { return plugin.getDescription().getVersion(); } @Override public boolean persist() { return true; } @Override public String onRequest(OfflinePlayer player, String identifier) { if (player == null) { return ""; } PlayerGroup group = plugin.getPlayerGroup(player.getPlayer()); switch (identifier) { case "group_members": return group != null ? group.getMembers().getNames().toString().substring(1, group.getMembers().getNames().toString().length() - 1) : ""; case "group_name": return group != null ? group.getName() : ""; case "group_name_raw": return group != null ? group.getRawName() : ""; case "group_player_count": return group != null ? String.valueOf(group.getMembers().size()) : ""; case "game_player_count": Game game = group.getGame(); return game != null ? String.valueOf(game.getPlayers().size()) : ""; case "floor_player_count": GameWorld gameWorld = group.getGameWorld(); return gameWorld != null ? String.valueOf(gameWorld.getPlayers().size()) : ""; case "dungeon_name": return group != null ? group.getDungeon().getName() : ""; case "global_dungeon_count": return String.valueOf(plugin.getDungeonRegistry().size()); case "global_floor_count": return String.valueOf(plugin.getMapRegistry().size()); case "global_instance_count": return String.valueOf(plugin.getInstanceCache().size()); default: return null; } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/DEditWorld.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.event.world.EditWorldSaveEvent; import de.erethon.dungeonsxl.api.event.world.EditWorldUnloadEvent; import de.erethon.dungeonsxl.api.event.world.InstanceWorldPostUnloadEvent; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.mob.CitizensMobProvider; import de.erethon.dungeonsxl.player.DEditPlayer; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.util.FileUtil; import de.erethon.xlib.util.ProgressBar; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author Frank Baumann, Daniel Saukel */ public class DEditWorld extends DInstanceWorld implements EditWorld { public static String ID_FILE_PREFIX = ".id_"; private File idFile; DEditWorld(DungeonsXL plugin, DResourceWorld resourceWorld, File folder) { super(plugin, resourceWorld, folder); } /* Getters and setters */ /** * Returns the file that stores the ID * * @return the file that stores the ID */ public File getIdFile() { return idFile; } /** * Generates an ID file for identification upon server restarts */ public void generateIdFile() { try { idFile = new File(getFolder(), ID_FILE_PREFIX + getName()); idFile.createNewFile(); } catch (IOException exception) { exception.printStackTrace(); } } /* Actions */ @Override public void registerSign(Block block) { if (block.getState() instanceof Sign) { Sign sign = (Sign) block.getState(); String[] lines = sign.getLines(); if (lines[0].equalsIgnoreCase("[lobby]")) { setLobbyLocation(block.getLocation()); } } } @Override public void save() { EditWorldSaveEvent event = new EditWorldSaveEvent(this); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } plugin.setLoadingWorld(true); Map players = new HashMap<>(); getWorld().getPlayers().forEach(p -> players.put(p, new double[]{ p.getLocation().getX(), p.getLocation().getY(), p.getLocation().getZ(), p.getLocation().getYaw(), p.getLocation().getPitch() } )); kickAllPlayers(); getResource().editWorld = null; plugin.getInstanceCache().remove(this); getResource().getSignData().serializeSigns(signs.values()); Bukkit.unloadWorld(getWorld(), true); new ProgressBar(players.keySet(), plugin.getMainConfig().getEditInstanceRemovalDelay()) { @Override public void onFinish() { getResource().clearFolder(); FileUtil.copyDir(getFolder(), getResource().getFolder(), DungeonsXL.EXCLUDED_FILES); DResourceWorld.deleteUnusedFiles(getResource().getFolder()); FileUtil.removeDir(getFolder()); plugin.setLoadingWorld(false); EditWorld newEditWorld = getResource().getOrInstantiateEditWorld(true); players.keySet().forEach(p -> { if (p.isOnline()) { new DEditPlayer(plugin, p, newEditWorld); double[] coords = players.get(p); p.teleport(new Location(newEditWorld.getWorld(), coords[0], coords[1], coords[2], (float) coords[3], (float) coords[4])); } }); } }.send(plugin); } public void forceSave() { EditWorldSaveEvent event = new EditWorldSaveEvent(this); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } getWorld().save(); FileUtil.copyDir(getFolder(), getResource().getFolder(), DungeonsXL.EXCLUDED_FILES); DResourceWorld.deleteUnusedFiles(getResource().getFolder()); getResource().getSignData().serializeSigns(signs.values()); } @Override public void delete() { delete(true); } @Override public void delete(boolean save) { EditWorldUnloadEvent event = new EditWorldUnloadEvent(this, true); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } if (Bukkit.getPluginManager().getPlugin("Citizens") != null) { ((CitizensMobProvider) plugin.getExternalMobProviderRegistry().get("CI")).removeSpawnedNPCs(getWorld()); } kickAllPlayers(); String name = getWorld().getName(); if (save) { getResource().getSignData().serializeSigns(signs.values()); boolean unloaded = Bukkit.unloadWorld(getWorld(), true); if (!unloaded) { MessageUtil.debug(plugin, "Error: Could not unload world " + getWorld()); } new BukkitRunnable() { @Override public void run() { getResource().clearFolder(); FileUtil.copyDir(getFolder(), getResource().getFolder(), DungeonsXL.EXCLUDED_FILES); DResourceWorld.deleteUnusedFiles(getResource().getFolder()); if (unloaded) { FileUtil.removeDir(getFolder()); } Bukkit.getPluginManager().callEvent(new InstanceWorldPostUnloadEvent(getResource(), name)); } }.runTaskLater(plugin, plugin.getMainConfig().getEditInstanceRemovalDelay() * 20L); } if (!save) { boolean unloaded = Bukkit.unloadWorld(getWorld(), /* SPIGOT-5225 */ !Version.isAtLeast(Version.MC1_14_4)); if (!unloaded) { MessageUtil.debug(plugin, "Error: Could not unload world " + getWorld()); } new BukkitRunnable() { @Override public void run() { if (unloaded) { FileUtil.removeDir(getFolder()); } Bukkit.getPluginManager().callEvent(new InstanceWorldPostUnloadEvent(getResource(), name)); } }.runTaskLater(plugin, plugin.getMainConfig().getEditInstanceRemovalDelay() * 20L); } getResource().editWorld = null; plugin.getInstanceCache().remove(this); } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/DGameWorld.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.BuildMode; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.event.trigger.TriggerRegistrationEvent; import de.erethon.dungeonsxl.api.event.world.GameWorldStartGameEvent; import de.erethon.dungeonsxl.api.event.world.InstanceWorldPostUnloadEvent; import de.erethon.dungeonsxl.api.event.world.InstanceWorldUnloadEvent; import de.erethon.dungeonsxl.api.mob.DungeonMob; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.trigger.LogicalExpression; import de.erethon.dungeonsxl.api.trigger.Trigger; import de.erethon.dungeonsxl.api.trigger.TriggerListener; import de.erethon.dungeonsxl.api.trigger.TriggerTypeKey; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.mob.CitizensMobProvider; import de.erethon.dungeonsxl.sign.button.ReadySign; import de.erethon.dungeonsxl.sign.passive.StartSign; import de.erethon.dungeonsxl.sign.windup.MobSign; import de.erethon.dungeonsxl.trigger.FortuneTrigger; import de.erethon.dungeonsxl.trigger.ProgressTrigger; import de.erethon.dungeonsxl.trigger.RedstoneTrigger; import de.erethon.dungeonsxl.util.BlockUtilCompat; import de.erethon.dungeonsxl.world.block.GameBlock; import de.erethon.dungeonsxl.world.block.LockedDoor; import de.erethon.dungeonsxl.world.block.MultiBlock; import de.erethon.dungeonsxl.world.block.PlaceableBlock; import de.erethon.dungeonsxl.world.block.RewardChest; import de.erethon.dungeonsxl.world.block.TeamBed; import de.erethon.dungeonsxl.world.block.TeamFlag; import de.erethon.xlib.XLib; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.util.FileUtil; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.Sign; import org.bukkit.entity.Entity; import org.bukkit.entity.Hanging; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; /** * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class DGameWorld extends DInstanceWorld implements GameWorld { private XLib xlib; private Game game; private Type type = Type.DEFAULT; private boolean isPlaying = false; private boolean classes = false; private Set placedBlocks = new HashSet<>(); private Set gameBlocks = new HashSet<>(); private Set lockedDoors = new HashSet<>(); private Set placeableBlocks = new HashSet<>(); private Set rewardChests = new HashSet<>(); private Set teamBeds = new HashSet<>(); private Set teamFlags = new HashSet<>(); private List secureObjects = new ArrayList<>(); private List mobs = new ArrayList<>(); private List triggers = new ArrayList<>(); private boolean readySign; DGameWorld(DungeonsXL plugin, DResourceWorld resourceWorld, File folder, Game game) { super(plugin, resourceWorld, folder); xlib = plugin.getXLib(); if (game == null) { throw new IllegalArgumentException("Game must not be null"); } this.game = game; } @Override public Type getType() { return type; } @Override public void setType(Type type) { this.type = type; } @Override public Game getGame() { return game; } @Override public Location getStartLocation(PlayerGroup dGroup) { int index = getGame().getGroups().indexOf(dGroup); // Try the matching location StartSign anyStartSign = null; for (DungeonSign sign : getDungeonSigns()) { if (sign instanceof StartSign) { anyStartSign = (StartSign) sign; if (anyStartSign.getId() == index) { return anyStartSign.getTargetLocation(); } } } // Try any start sign if (anyStartSign != null) { return anyStartSign.getTargetLocation(); } // Lobby location as fallback if (getLobbyLocation() != null) { return getLobbyLocation(); } return getWorld().getSpawnLocation(); } @Override public boolean areClassesEnabled() { return classes; } @Override public void setClassesEnabled(boolean enabled) { classes = enabled; } @Override public DungeonSign createDungeonSign(Sign sign, String[] lines) { DungeonSign dSign = super.createDungeonSign(sign, lines); if (dSign == null) { return null; } LogicalExpression expression = null; if (!dSign.isTriggerLineDisabled()) { try { expression = LogicalExpression.parse(lines[3]); } catch (IllegalArgumentException exception) { dSign.markAsErroneous("The trigger string " + lines[3] + " is invalid."); } } createTriggers(dSign, expression); for (Trigger trigger : triggers) { trigger.addListener(dSign); } if (dSign.isOnDungeonInit()) { try { dSign.initialize(); } catch (Exception exception) { dSign.markAsErroneous("An error occurred while initializing a sign of the type " + dSign.getName() + ". This is not a user error. Please report the following stacktrace to the developer of the plugin:"); exception.printStackTrace(); } if (!dSign.isErroneous() && dSign.isSetToAir()) { dSign.setToAir(); } } return dSign; } @Override public Trigger createTrigger(TriggerListener owner, LogicalExpression expression) { if (!expression.isAtomic()) { throw new IllegalArgumentException("Expression is not atomic"); } String text = expression.getText(); if (text.isBlank()) { return null; } char key = Character.toUpperCase(text.charAt(0)); String value; if (plugin.getTriggerRegistry().containsKey(key)) { value = text.substring(1, text.length() - 1); } else { key = 'T'; value = text; } Trigger trigger = getTrigger(key, value); if (trigger != null) { return trigger; } Class clss = plugin.getTriggerRegistry().get(key); if (clss == null) { return null; } // Legacy shit if (key == TriggerTypeKey.PROGRESS && value.matches("[0-99]/[0-999]")) { trigger = ProgressTrigger.getOrCreate(plugin, owner, expression, value); } else { trigger = Trigger.construct(key, plugin, owner, expression, value); } if (trigger == null) { return null; } TriggerRegistrationEvent event = new TriggerRegistrationEvent(trigger); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { triggers.add(trigger); } return trigger; } @Override public List createTriggers(TriggerListener owner, LogicalExpression expression) { List atomicExpressions = expression.getContents(true); List created = new ArrayList<>(atomicExpressions.size()); for (LogicalExpression atomic : atomicExpressions) { Trigger trigger = atomic.toTrigger(plugin, owner, true); created.add(trigger); } return created; } public Trigger getTrigger(char key, String value) { if (!Trigger.IDENTIFIABLE.contains(key)) { return null; } for (Trigger trigger : triggers) { if (trigger.getKey() != key) { continue; } if (trigger.getValue().equalsIgnoreCase(value)) { return trigger; } } return null; } @Override public Collection getPlacedBlocks() { return placedBlocks; } /** * @return the placeableBlocks */ public Set getGameBlocks() { return gameBlocks; } /** * @param gameBlock the gameBlock to add */ public void addGameBlock(GameBlock gameBlock) { gameBlocks.add(gameBlock); if (gameBlock instanceof LockedDoor) { lockedDoors.add((LockedDoor) gameBlock); } else if (gameBlock instanceof PlaceableBlock) { placeableBlocks.add((PlaceableBlock) gameBlock); } else if (gameBlock instanceof RewardChest) { rewardChests.add((RewardChest) gameBlock); } else if (gameBlock instanceof TeamBed) { teamBeds.add((TeamBed) gameBlock); } else if (gameBlock instanceof TeamFlag) { teamFlags.add((TeamFlag) gameBlock); } } /** * @param gameBlock the gameBlock to remove */ public void removeGameBlock(GameBlock gameBlock) { gameBlocks.remove(gameBlock); if (gameBlock instanceof LockedDoor) { lockedDoors.remove((LockedDoor) gameBlock); } else if (gameBlock instanceof PlaceableBlock) { placeableBlocks.remove((PlaceableBlock) gameBlock); } else if (gameBlock instanceof RewardChest) { rewardChests.remove((RewardChest) gameBlock); } else if (gameBlock instanceof TeamBed) { teamBeds.remove((TeamBed) gameBlock); } else if (gameBlock instanceof TeamFlag) { teamFlags.remove((TeamFlag) gameBlock); } } /** * @return the rewardChests */ public Set getRewardChests() { return rewardChests; } /** * @return the locked doors */ public Set getLockedDoors() { return lockedDoors; } /** * @return the placeable blocks */ public Set getPlaceableBlocks() { return placeableBlocks; } /** * @return the team beds */ public Set getTeamBeds() { return teamBeds; } /** * @return the team flags */ public Set getTeamFlags() { return teamFlags; } /** * @return the secureObjects */ public List getSecureObjects() { return secureObjects; } /** * @param secureObjects the secureObjects to set */ public void setSecureObjects(List secureObjects) { this.secureObjects = secureObjects; } @Override public Collection getMobs() { return mobs; } @Override public void addMob(DungeonMob mob) { mobs.add(mob); } @Override public void removeMob(DungeonMob mob) { mobs.remove(mob); } @Override public boolean isPlaying() { return isPlaying; } public void setPlaying(boolean isPlaying) { this.isPlaying = isPlaying; } @Override public Collection getTriggers() { return triggers; } @Override public Collection getTriggersFromKey(char key) { return triggers.stream() .filter(t -> t.getKey() == key) .toList(); } @Override public boolean unregisterTrigger(Trigger trigger) { return triggers.remove(trigger); } /** * @return the potential amount of mobs in the world */ public int getMobCount() { int mobCount = 0; signs: for (DungeonSign sign : getDungeonSigns().toArray(DungeonSign[]::new)) { if (!(sign instanceof MobSign)) { continue; } for (Trigger trigger : sign.getTriggers()) { if (trigger instanceof ProgressTrigger) { if (((ProgressTrigger) trigger).getFloorCount() > getGame().getFloorCount()) { break signs; } } } mobCount += ((MobSign) sign).getInitialAmount(); } return mobCount; } @Override public Dungeon getDungeon() { if (getGame() != null) { return getGame().getDungeon(); } for (Dungeon dungeon : plugin.getDungeonRegistry()) { if (dungeon.containsFloor(getResource())) { return dungeon; } } return null; } public boolean hasReadySign() { return readySign; } public void setReadySign(ReadySign readySign) { this.readySign = readySign != null; } /** * Set up the instance for the game */ public void startGame() { GameWorldStartGameEvent event = new GameWorldStartGameEvent(this, getGame()); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } getWorld().setDifficulty(getRules().getState(GameRule.DIFFICULTY)); Boolean doFireTick = getRules().getState(GameRule.FIRE_TICK); if (Version.isAtLeast(Version.MC1_21_11)) { getWorld().setGameRule(org.bukkit.GameRule.FIRE_SPREAD_RADIUS_AROUND_PLAYER, (doFireTick ? -1 : 0)); } else if (Version.isAtLeast(Version.MC1_13)) { getWorld().setGameRule((org.bukkit.GameRule) org.bukkit.GameRule.getByName("DO_FIRE_TICK"), doFireTick); } isPlaying = true; for (DungeonSign sign : getDungeonSigns().toArray(new DungeonSign[getDungeonSigns().size()])) { if (sign == null || sign.isOnDungeonInit()) { continue; } try { sign.initialize(); } catch (Exception exception) { sign.markAsErroneous("An error occurred while initializing a sign of the type " + sign.getName() + ". This is not a user error. Please report the following stacktrace to the developer of the plugin:"); exception.printStackTrace(); } if (sign.isErroneous()) { continue; } if (sign.isSetToAir()) { sign.setToAir(); } if (!sign.hasTriggers()) { try { sign.trigger(null); } catch (Exception exception) { sign.markAsErroneous("An error occurred while triggering a sign of the type " + getName() + ". This is not a user error. Please report the following stacktrace to the developer of the plugin:"); exception.printStackTrace(); } } } for (Trigger trigger : getTriggersFromKey(TriggerTypeKey.REDSTONE)) { ((RedstoneTrigger) trigger).trigger(true, null); } for (Trigger trigger : getTriggersFromKey(TriggerTypeKey.FORTUNE)) { ((FortuneTrigger) trigger).trigger(true, null); } } /** * Delete this instance. */ @Override public void delete() { InstanceWorldUnloadEvent event = new InstanceWorldUnloadEvent(this); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return; } if (Bukkit.getPluginManager().getPlugin("Citizens") != null) { ((CitizensMobProvider) plugin.getExternalMobProviderRegistry().get("CI")).removeSpawnedNPCs(getWorld()); } kickAllPlayers(); getWorld().getEntities().forEach(Entity::remove); String name = getWorld().getName(); boolean unloaded = Bukkit.unloadWorld(getWorld(), /* SPIGOT-5225 */ !Version.isAtLeast(Version.MC1_14_4)); if (unloaded) { FileUtil.removeDir(getFolder()); } else { MessageUtil.debug(plugin, "Error: World could not be unloaded, players left in world: " + !getWorld().getPlayers().isEmpty()); } plugin.getInstanceCache().remove(this); Bukkit.getPluginManager().callEvent(new InstanceWorldPostUnloadEvent(getResource(), name)); } private GameRuleContainer getRules() { return getDungeon().getRules(); } /** * Handles what happens when a player breaks a block. * * @param event the passed Bukkit event * @return if the event is cancelled */ public boolean onBreak(BlockBreakEvent event) { Player player = event.getPlayer(); Block block = event.getBlock(); for (DungeonSign sign : getDungeonSigns()) { if (sign == null) { continue; } if ((block.equals(sign.getSign().getBlock()) || block.equals(BlockUtilCompat.getAttachedBlock(sign.getSign().getBlock()))) && sign.isProtected()) { return true; } } for (GameBlock gameBlock : gameBlocks) { if (block.equals(gameBlock.getBlock())) { if (gameBlock.onBreak(event)) { return true; } } else if (gameBlock instanceof MultiBlock) { if (block.equals(((MultiBlock) gameBlock).getAttachedBlock())) { if (gameBlock.onBreak(event)) { return true; } } } } Game game = getGame(); if (game == null) { return true; } BuildMode mode = getRules().getState(GameRule.BREAK_BLOCKS); if (mode == BuildMode.FALSE) { return true; } // Cancel if a protected entity is attached for (Entity entity : getWorld().getNearbyEntities(block.getLocation(), 2, 2, 2)) { if (!(entity instanceof Hanging)) { continue; } if (entity.getLocation().getBlock().getRelative(((Hanging) entity).getAttachedFace()).equals(block)) { Hanging hanging = (Hanging) entity; if (getRules().getState(GameRule.DAMAGE_PROTECTED_ENTITIES).contains(xlib.getExMob(hanging))) { event.setCancelled(true); break; } } } boolean breakBlock = !mode.check(player, this, block); if (breakBlock) { placedBlocks.remove(block); } return breakBlock; } /** * Handles what happens when a player places a block. * * @param player * @param block * @param against * @param hand the event parameters. * @return if the event is cancelled */ public boolean onPlace(Player player, Block block, Block against, ItemStack hand) { Game game = getGame(); if (game == null) { return true; } if (getRules().getState(GameRule.PLACE_BLOCKS).check(player, this, block)) { placedBlocks.add(block); return false; } PlaceableBlock placeableBlock = null; for (PlaceableBlock gamePlaceableBlock : placeableBlocks) { if (gamePlaceableBlock.canPlace(block, xlib.getExItem(hand))) { placeableBlock = gamePlaceableBlock; break; } } if (placeableBlock == null) { // Workaround for a bug that would allow 3-Block-high jumping Location loc = player.getLocation(); if (loc.getY() > block.getY() + 1.0 && loc.getY() <= block.getY() + 1.5) { if (loc.getX() >= block.getX() - 0.3 && loc.getX() <= block.getX() + 1.3) { if (loc.getZ() >= block.getZ() - 0.3 && loc.getZ() <= block.getZ() + 1.3) { loc.setX(block.getX() + 0.5); loc.setY(block.getY()); loc.setZ(block.getZ() + 0.5); player.teleport(loc); } } } return true; } placeableBlock.onPlace(player); return false; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/DInstanceWorld.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.player.InstancePlayer; import de.erethon.dungeonsxl.api.player.PlayerCache; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.compatibility.Version; import java.io.File; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.Sign; /** * @author Daniel Saukel */ public abstract class DInstanceWorld implements InstanceWorld { protected DungeonsXL plugin; protected PlayerCache dPlayers; private static int counter; protected Map signs = new HashMap<>(); private DResourceWorld resourceWorld; private File folder; String world; private int id; private Location lobby; DInstanceWorld(DungeonsXL plugin, DResourceWorld resourceWorld, File folder) { this.plugin = plugin; dPlayers = plugin.getPlayerCache(); this.resourceWorld = resourceWorld; this.folder = folder; id = counter++; plugin.getInstanceCache().add(id, this); } /* Getters and setters */ @Override public String getName() { return resourceWorld.getName(); } @Override public DResourceWorld getResource() { return resourceWorld; } @Override public File getFolder() { return folder; } @Override public World getWorld() { if (world == null) { return null; } return Bukkit.getWorld(world); } /** * Returns false if this instance does not have a world, yet * * @return false if this instance does not have a world, yet */ public boolean exists() { return world != null; } @Override public int getId() { return id; } @Override public Collection getDungeonSigns() { return signs.values(); } @Override public DungeonSign createDungeonSign(Sign sign, String[] lines) { String type = lines[0].substring(1, lines[0].length() - 1); try { Class clss = plugin.getSignRegistry().get(type.toUpperCase()); if (clss == null) { return null; } Constructor constructor = clss.getConstructor(DungeonsAPI.class, Sign.class, String[].class, InstanceWorld.class); DungeonSign dSign = (DungeonSign) constructor.newInstance(plugin, sign, lines, this); signs.put(sign.getBlock(), dSign); return dSign; } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { MessageUtil.log(plugin, "&4Could not create a dungeon sign of the type \"" + type + "\". A dungeon sign implementation needs a constructor with the types (DungeonsAPI, org.bukkit.block.Sign, String[], InstanceWorld)."); return null; } } @Override public void removeDungeonSign(DungeonSign sign) { signs.remove(sign.getSign().getBlock()); } @Override public void removeDungeonSign(Block sign) { signs.remove(sign); } @Override public DungeonSign getDungeonSign(Block sign) { return signs.get(sign); } @Override public Location getLobbyLocation() { return lobby; } @Override public void setLobbyLocation(Location lobby) { this.lobby = lobby; } @Override public Collection getPlayers() { return plugin.getPlayerCache().getAllInstancePlayersIf(p -> p.getInstanceWorld() == this); } /* Actions */ @Override public void sendMessage(String message) { getPlayers().forEach(p -> MessageUtil.sendMessage(p.getPlayer(), message)); } @Override public void kickAllPlayers() { getPlayers().forEach(p -> p.leave()); // Players who shouldn't be in the dungeon but still are for some reason if (plugin.isLoaded() && world != null) { getWorld().getPlayers().forEach(p -> p.teleport(Bukkit.getWorlds().get(0).getSpawnLocation())); } } /** * @param rules sets up the time and weather to match the rules */ public void setWeather(GameRuleContainer rules) { if (world == null || getWorld() == null) { return; } if (rules.getState(GameRule.THUNDER) != null) { if (rules.getState(GameRule.THUNDER)) { getWorld().setThundering(true); getWorld().setStorm(true); getWorld().setThunderDuration(Integer.MAX_VALUE); } else { getWorld().setThundering(false); getWorld().setStorm(false); } } if (rules.getState(GameRule.TIME) != null) { getWorld().setTime(rules.getState(GameRule.TIME)); } } /** * @return a name for the instance * @param game whether the instance is a GameWorld */ public static String generateName(boolean game) { String name = "DXL_" + (game ? "Game" : "Edit") + "_" + counter; File instanceFolder = new File(Bukkit.getWorldContainer(), name); while (instanceFolder.exists()) { if (Bukkit.getWorld(name) != null) { Bukkit.unloadWorld(name, /* SPIGOT-5225 */ !Version.isAtLeast(Version.MC1_14_4)); } if (!instanceFolder.delete()) { MessageUtil.log(DungeonsXL.getInstance(), "&6Warning: An unrecognized junk instance (&4" + name + "&6) has been found, but could not be deleted."); } counter++; name = "DXL_" + (game ? "Game" : "Edit") + "_" + counter; instanceFolder = new File(Bukkit.getWorldContainer(), name); } return name; } @Override public String toString() { return getClass().getSimpleName() + "{name=" + getName() + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/DResourceWorld.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.Dungeon; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.event.world.EditWorldGenerateEvent; import de.erethon.dungeonsxl.api.event.world.ResourceWorldInstantiateEvent; import de.erethon.dungeonsxl.api.player.EditPlayer; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.ResourceWorld; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.util.FileUtil; import java.io.File; import java.io.IOException; import org.bukkit.Bukkit; import org.bukkit.GameRule; import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.World.Environment; import org.bukkit.WorldCreator; import org.bukkit.WorldType; /** * @author Daniel Saukel */ public class DResourceWorld implements ResourceWorld { public static final File RAW = new File(DungeonsXL.MAPS, ".raw"); private DungeonsXL plugin; private File folder; private WorldConfig config; private SignData signData; EditWorld editWorld; public DResourceWorld(DungeonsXL plugin, String name) { this.plugin = plugin; folder = new File(DungeonsXL.MAPS, name); if (!folder.exists()) { folder.mkdir(); } File configFile = new File(folder, WorldConfig.FILE_NAME); if (configFile.exists()) { config = new WorldConfig(plugin, configFile); } signData = new SignData(new File(folder, SignData.FILE_NAME)); } public DResourceWorld(DungeonsXL plugin, File folder) { this.plugin = plugin; this.folder = folder; File configFile = new File(folder, WorldConfig.FILE_NAME); if (configFile.exists()) { config = new WorldConfig(plugin, configFile); } signData = new SignData(new File(folder, SignData.FILE_NAME)); } /* Getters and setters */ @Override public String getName() { return folder.getName(); } @Override public void setName(String name) { folder.renameTo(new File(folder.getParentFile(), name)); folder = new File(folder.getParentFile(), name); } @Override public File getFolder() { return folder; } @Override public GameRuleContainer getRules() { return getConfig(false); } /** * Returns the config of this world. * * @param generate if a config should be generated if none exists * @return the config of this world */ public WorldConfig getConfig(boolean generate) { if (config == null) { File file = new File(folder, WorldConfig.FILE_NAME); if (!file.exists() && generate) { try { file.createNewFile(); } catch (IOException exception) { exception.printStackTrace(); } } config = new WorldConfig(plugin, file); } return config; } @Override public Environment getWorldEnvironment() { return (getConfig(false) != null && getConfig(false).getWorldEnvironment() != null) ? getConfig(false).getWorldEnvironment() : Environment.NORMAL; } @Override public void addInvitedPlayer(OfflinePlayer player) { getConfig(true).addInvitedPlayer(player.getUniqueId().toString()); config.save(); } @Override public boolean removeInvitedPlayer(OfflinePlayer player) { if (config == null) { return false; } config.removeInvitedPlayers(player.getUniqueId().toString(), player.getName().toLowerCase()); config.save(); EditPlayer editPlayer = plugin.getPlayerCache().getEditPlayer(player.getPlayer()); if (editPlayer != null) { if (plugin.getEditWorld(editPlayer.getWorld()).getResource() == this) { editPlayer.leave(); } } return true; } @Override public boolean isInvitedPlayer(OfflinePlayer player) { if (config == null) { return false; } return config.getInvitedPlayers().contains(player.getName().toLowerCase()) || config.getInvitedPlayers().contains(player.getUniqueId().toString()); } /* Actions */ @Override public void backup() { File target = new File(DungeonsXL.BACKUPS, getName() + "-" + System.currentTimeMillis()); FileUtil.copyDir(folder, target); } public DInstanceWorld instantiate(Game game) { plugin.setLoadingWorld(true); String name = DInstanceWorld.generateName(game != null); File instanceFolder = new File(Bukkit.getWorldContainer(), name); DInstanceWorld instance = game != null ? new DGameWorld(plugin, this, instanceFolder, game) : new DEditWorld(plugin, this, instanceFolder); ResourceWorldInstantiateEvent event = new ResourceWorldInstantiateEvent(this, name); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return null; } FileUtil.copyDir(folder, instanceFolder, DungeonsXL.EXCLUDED_FILES); instance.world = Bukkit.createWorld(WorldCreator.name(name).environment(getWorldEnvironment())).getName(); if (Version.isAtLeast(Version.MC1_21_11)) { instance.getWorld().setGameRule(GameRule.FIRE_SPREAD_RADIUS_AROUND_PLAYER, 0); } else if (Version.isAtLeast(Version.MC1_13)) { instance.getWorld().setGameRule((GameRule) GameRule.getByName("DO_FIRE_TICK"), false); } if (Bukkit.getPluginManager().isPluginEnabled("dynmap")) { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "dynmap pause all"); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "dmap worldset " + name + " enabled:false"); Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "dynmap pause none"); } if (game != null) { signData.deserializeSigns((DGameWorld) instance); instance.getWorld().setAutoSave(false); } else { signData.deserializeSigns((DEditWorld) instance); } plugin.setLoadingWorld(false); return instance; } @Override public EditWorld getEditWorld() { return editWorld; } @Override public EditWorld getOrInstantiateEditWorld(boolean ignoreLimit) { if (editWorld != null) { return editWorld; } if (plugin.isLoadingWorld()) { return null; } if (!ignoreLimit && plugin.getMainConfig().getMaxInstances() <= plugin.getInstanceCache().size()) { return null; } editWorld = (EditWorld) instantiate(null); return editWorld; } @Override public GameWorld instantiateGameWorld(Game game, boolean ignoreLimit) { if (plugin.isLoadingWorld()) { return null; } if (!ignoreLimit && plugin.getMainConfig().getMaxInstances() <= plugin.getInstanceCache().size()) { return null; } return (DGameWorld) instantiate(game); } @Override public Dungeon getSingleFloorDungeon() { return plugin.getDungeonRegistry().get(getName()); } /** * Returns the DXLData.data file * * @return the DXLData.data file */ public SignData getSignData() { return signData; } /** * Generate a new DResourceWorld. * * @return the automatically created DEditWorld instance */ public DEditWorld generate() { String name = DInstanceWorld.generateName(false); File folder = new File(Bukkit.getWorldContainer(), name); WorldCreator creator = new WorldCreator(name); creator.type(WorldType.FLAT); creator.generateStructures(false); DEditWorld editWorld = new DEditWorld(plugin, this, folder); this.editWorld = editWorld; ResourceWorldInstantiateEvent event = new ResourceWorldInstantiateEvent(this, name); Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { return null; } if (!RAW.exists()) { createRaw(); } FileUtil.copyDir(RAW, folder, DungeonsXL.EXCLUDED_FILES); editWorld.generateIdFile(); editWorld.world = creator.createWorld().getName(); editWorld.generateIdFile(); Bukkit.getPluginManager().callEvent(new EditWorldGenerateEvent(editWorld)); return editWorld; } void clearFolder() { for (File file : FileUtil.getFilesForFolder(getFolder())) { if (file.getName().equals(SignData.FILE_NAME) || file.getName().equals(WorldConfig.FILE_NAME)) { continue; } if (file.isDirectory()) { FileUtil.removeDir(file); } else { file.delete(); } } } /** * Removes files that are not needed from a world * * @param dir the directory to purge */ public static void deleteUnusedFiles(File dir) { for (File file : dir.listFiles()) { if (file.getName().equalsIgnoreCase("uid.dat") || file.getName().contains(".id_")) { file.delete(); } } } /** * Creates the "raw" world that is copied for new instances. */ public static void createRaw() { WorldCreator rawCreator = WorldCreator.name(".raw"); rawCreator.type(WorldType.FLAT); rawCreator.generateStructures(false); World world = rawCreator.createWorld(); File worldFolder = new File(Bukkit.getWorldContainer(), ".raw"); FileUtil.copyDir(worldFolder, RAW, DungeonsXL.EXCLUDED_FILES); Bukkit.unloadWorld(world, /* SPIGOT-5225 */ !Version.isAtLeast(Version.MC1_14_4)); FileUtil.removeDir(worldFolder); } @Override public String toString() { return "DResourceWorld{name=" + getName() + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/DWorldListener.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.dungeonsxl.api.world.EditWorld; import de.erethon.dungeonsxl.api.world.GameWorld; import de.erethon.dungeonsxl.api.world.InstanceWorld; import de.erethon.dungeonsxl.player.DPlayerListener; import de.erethon.dungeonsxl.util.ContainerAdapter; import de.erethon.xlib.XLib; import de.erethon.xlib.category.Category; import de.erethon.xlib.compatibility.Version; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.mob.ExMob; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockFadeEvent; import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.event.hanging.HangingBreakEvent; import org.bukkit.event.player.PlayerArmorStandManipulateEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.weather.WeatherChangeEvent; import org.bukkit.event.world.WorldInitEvent; import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.PlayerInventory; /** * @author Daniel Saukel, Frank Baumann, Milan Albrecht */ public class DWorldListener implements Listener { private DungeonsXL plugin; private XLib xlib; public DWorldListener(DungeonsXL plugin) { this.plugin = plugin; xlib = plugin.getXLib(); } @EventHandler(priority = EventPriority.HIGHEST) public void onInit(WorldInitEvent event) { World world = event.getWorld(); if (plugin.isInstance(world)) { world.setKeepSpawnInMemory(false); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockBreak(BlockBreakEvent event) { Block block = event.getBlock(); // EditWorld Signs EditWorld editWorld = plugin.getEditWorld(block.getWorld()); if (editWorld != null) { editWorld.removeDungeonSign(event.getBlock()); return; } // Deny GameWorld block breaking DGameWorld gameWorld = (DGameWorld) plugin.getGameWorld(block.getWorld()); if (gameWorld != null) { if (gameWorld.onBreak(event)) { event.setCancelled(true); } } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); if (DPlayerListener.isCitizensNPC(player)) { return; } Block block = event.getClickedBlock(); if (block == null) { return; } GameWorld gameWorld = plugin.getGameWorld(block.getWorld()); if (gameWorld == null) { return; } if (!gameWorld.isPlaying()) { if (!plugin.getMainConfig().areLobbyContainersEnabled() || !ContainerAdapter.isValidContainer(block)) { event.setCancelled(true); } return; } Map> blacklist = gameWorld.getDungeon().getRules().getState(GameRule.INTERACTION_BLACKLIST); if (blacklist.isEmpty()) { return; } ExItem material = VanillaItem.get(block.getType()); ExItem tool = xlib.getExItem(getItemInHand(event)); if (blacklist.containsKey(material) && (blacklist.get(material) == null || blacklist.get(material).isEmpty() || blacklist.get(material).contains(tool))) { event.setCancelled(true); } } private ItemStack getItemInHand(PlayerInteractEvent event) { PlayerInventory inventory = event.getPlayer().getInventory(); if (Version.isAtLeast(Version.MC1_9)) { return event.getHand() == EquipmentSlot.HAND ? inventory.getItemInMainHand() : inventory.getItemInOffHand(); } else { return inventory.getItemInHand(); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockPlace(BlockPlaceEvent event) { Block block = event.getBlock(); DGameWorld gameWorld = (DGameWorld) plugin.getGameWorld(block.getWorld()); if (gameWorld == null) { return; } if (gameWorld.onPlace(event.getPlayer(), block, event.getBlockAgainst(), event.getItemInHand())) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockIgnite(BlockIgniteEvent event) { if (!plugin.isInstance(event.getBlock().getWorld())) { return; } if (event.getCause() != BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onBlockSpread(BlockSpreadEvent event) { Block block = event.getSource(); if (plugin.isInstance(block.getWorld()) && VanillaItem.VINE.is(block)) { event.setCancelled(true); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onEntityExplode(EntityExplodeEvent event) { if (plugin.getGameWorld(event.getEntity().getWorld()) == null) { return; } if (event.getEntity() instanceof LivingEntity) { // Disable Creeper explosions in gameWorlds event.setCancelled(true); } else {// TODO respect block breaking game rules // Disable drops from TNT event.setYield(0); } } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onEntityDamage(EntityDamageEvent event) { onTouch(event, event.getEntity(), false); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onHangingBreak(HangingBreakEvent event) { onTouch(event, event.getEntity(), false); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onPlayerInteractEntity(PlayerInteractEntityEvent event) { onTouch(event, event.getRightClicked(), true); } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onArmorStandManipulate(PlayerArmorStandManipulateEvent event) { onTouch(event, event.getRightClicked(), true); } /** * @param event the event * @param entity the entity * @param interact true = interact; false = break */ public void onTouch(Cancellable event, Entity entity, boolean interact) { GameWorld gameWorld = plugin.getGameWorld(entity.getWorld()); if (gameWorld == null) { return; } GameRuleContainer rules = gameWorld.getDungeon().getRules(); Set prot = interact ? rules.getState(GameRule.INTERACTION_PROTECTED_ENTITIES) : rules.getState(GameRule.DAMAGE_PROTECTED_ENTITIES); if (prot.contains(xlib.getExMob(entity))) { event.setCancelled(true); } } // TODO: Is this necessary? @EventHandler public void onItemSpawn(ItemSpawnEvent event) { if (plugin.getGameWorld(event.getLocation().getWorld()) != null) { if (Category.SIGNS.containsItem(event.getEntity().getItemStack())) { event.setCancelled(true); } } } @EventHandler public void onBlockFade(BlockFadeEvent event) { GameWorld gameWorld = plugin.getGameWorld(event.getBlock().getWorld()); if (gameWorld == null) { return; } if (!gameWorld.isPlaying()) { event.setCancelled(true); return; } Set blockFadeDisabled = gameWorld.getGame().getRules().getState(GameRule.BLOCK_FADE_DISABLED); if (blockFadeDisabled.isEmpty()) { return; } if (gameWorld.getGame() != null && blockFadeDisabled.contains(VanillaItem.get(event.getBlock().getType()))) { event.setCancelled(true); } } @EventHandler public void onWeatherChange(WeatherChangeEvent event) { InstanceWorld instance = plugin.getInstanceWorld(event.getWorld()); if (instance instanceof EditWorld && event.toWeatherState()) { event.setCancelled(true); } else if (instance instanceof GameWorld) { Boolean raining = ((GameWorld) instance).getDungeon().getRules().getState(GameRule.RAIN); if (raining == null) { return; } if ((raining && !event.toWeatherState()) || (!raining && event.toWeatherState())) { event.setCancelled(true); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/LWCIntegration.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import com.griefcraft.lwc.LWC; import com.griefcraft.scripting.JavaModule; import com.griefcraft.scripting.event.LWCProtectionRegisterEvent; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.world.InstanceWorld; /** * @author Daniel Saukel */ public class LWCIntegration extends JavaModule { private DungeonsXL plugin; public LWCIntegration(DungeonsXL plugin) { this.plugin = plugin; LWC.getInstance().getModuleLoader().registerModule(plugin, this); } @Override public void onRegisterProtection(LWCProtectionRegisterEvent event) { InstanceWorld instance = plugin.getInstanceWorld(event.getBlock().getWorld()); if (instance != null) { event.setCancelled(true); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/SignData.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.api.sign.DungeonSign; import de.erethon.dungeonsxl.api.world.InstanceWorld; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collection; import org.bukkit.block.Block; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; /** * Represents the data file of a dungeon map, mainly to store signs. * * @author Daniel Saukel */ public class SignData { public static final String FILE_NAME = "DXLData.data"; private File file; public SignData(File file) { if (!file.exists()) { try { file.createNewFile(); } catch (IOException exception) { exception.printStackTrace(); } } this.file = file; } public File getFile() { return file; } public void updateFile(DResourceWorld resource) { file = new File(resource.getFolder(), FILE_NAME); } /** * Loads all signs from the file to the instance. * * @param instance the instance where the signs are */ public void deserializeSigns(InstanceWorld instance) { try { ObjectInputStream os = new ObjectInputStream(new FileInputStream(file)); int length = os.readInt(); for (int i = 0; i < length; i++) { int x = os.readInt(); int y = os.readInt(); int z = os.readInt(); Block block = instance.getWorld().getBlockAt(x, y, z); BlockState state = block.getState(); if (state instanceof Sign) { Sign sign = (Sign) state; String[] lines = sign.getLines(); instance.createDungeonSign(sign, lines); if (lines[0].equalsIgnoreCase("[lobby]")) { instance.setLobbyLocation(block.getLocation()); } } } os.close(); } catch (IOException exception) { exception.printStackTrace(); } } /** * Saves all signs from an instance to the file. * * @param instance the instance that contains the signs to serialize */ public void serializeSigns(InstanceWorld instance) { serializeSigns(instance.getDungeonSigns()); } /** * Saves all signs from the sign list to the file. * * @param signs the signs to serialize */ public void serializeSigns(Collection signs) { try { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(file)); out.writeInt(signs.size()); for (DungeonSign sign : signs) { out.writeInt(sign.getSign().getX()); out.writeInt(sign.getSign().getY()); out.writeInt(sign.getSign().getZ()); } out.close(); } catch (IOException exception) { exception.printStackTrace(); } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/WorldConfig.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.dungeon.GameRule; import de.erethon.dungeonsxl.api.dungeon.GameRuleContainer; import de.erethon.xlib.util.EnumUtil; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.bukkit.World.Environment; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; /** * The world configuration is a simple game rule source. Besides game rules, WorldConfig also stores some map specific data such as the invited players. It is * used directly in dungeon map config.yml files, but also part of dungeon and main config files. * * @author Frank Baumann, Milan Albrecht, Daniel Saukel */ public class WorldConfig extends GameRuleContainer { private DungeonsXL plugin; public static final String FILE_NAME = "config.yml"; private File file; private ConfigurationSection config; private List invitedPlayers = new ArrayList<>(); private Environment worldEnvironment; public WorldConfig(DungeonsXL plugin, File file) { this.plugin = plugin; this.file = file; config = YamlConfiguration.loadConfiguration(file); load(); } public WorldConfig(DungeonsXL plugin, ConfigurationSection config) { this.plugin = plugin; if (config == null) { config = new YamlConfiguration(); } this.config = config; load(); } public void load() { plugin.getGameRuleRegistry().forEach(this::updateGameRule); invitedPlayers = config.getStringList("invitedPlayers"); worldEnvironment = EnumUtil.getEnumIgnoreCase(Environment.class, config.getString("worldEnvironment", Environment.NORMAL.name())); } public void updateGameRule(GameRule rule) { rule.fromConfig(plugin, this, config); } public void save() { if (file == null) { return; } FileConfiguration configFile = YamlConfiguration.loadConfiguration(file); if (getState(GameRule.MESSAGES) != null) { for (int msgs : getState(GameRule.MESSAGES).keySet()) { configFile.set("messages." + msgs, getState(GameRule.MESSAGES).get(msgs)); } } configFile.set("invitedPlayers", invitedPlayers); if (worldEnvironment != null) { configFile.set("worldEnvironment", worldEnvironment.name()); } try { configFile.save(file); } catch (IOException exception) { exception.printStackTrace(); } } /** * @return the UUIDs or names of the players invited to edit the map */ public List getInvitedPlayers() { return new ArrayList<>(invitedPlayers); } /** * @param uuid the player's unique ID */ public void addInvitedPlayer(String uuid) { if (!invitedPlayers.contains(uuid)) { invitedPlayers.add(uuid); } } /** * @param uuid the player's unique ID * @param name the player's name */ public void removeInvitedPlayers(String uuid, String name) { invitedPlayers.remove(uuid); invitedPlayers.remove(name); } /** * @return the world environment */ public Environment getWorldEnvironment() { return worldEnvironment; } /** * @param worldEnvironment the world environment to set */ public void setWorldEnvironment(Environment worldEnvironment) { this.worldEnvironment = worldEnvironment; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/GameBlock.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.api.DungeonsAPI; import org.bukkit.block.Block; import org.bukkit.event.block.BlockBreakEvent; /** * A block that has a special purpose in a game. * * @author Daniel Saukel */ public abstract class GameBlock { protected DungeonsAPI api; protected Block block; public GameBlock(DungeonsAPI api, Block block) { this.api = api; this.block = block; } /* Getters and setters */ /** * @return the block */ public Block getBlock() { return block; } /** * @param block the block to set */ public void setBlock(Block block) { this.block = block; } /* Abstracts */ /** * Handles what happens when a player breaks the block. * * @param event the passed Bukkit event * @return if the event is cancelled */ public abstract boolean onBreak(BlockBreakEvent event); @Override public String toString() { return getClass().getSimpleName() + "{block=" + block + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/LockedDoor.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.event.block.BlockBreakEvent; /** * A locked door that may be opened with a trigger. * * @author Daniel Saukel */ public class LockedDoor extends GameBlock implements MultiBlock { private Block attachedBlock; public LockedDoor(DungeonsAPI api, Block block) { super(api, block); attachedBlock = getAttachedBlock(); } /* Getters and setters */ @Override public Block getAttachedBlock() { if (attachedBlock != null) { return attachedBlock; } else { return block.getRelative(BlockFace.UP); } } /* Actions */ @Override public boolean onBreak(BlockBreakEvent event) { return true; } /** * Opens the door. */ public void open() { DungeonsXL.BLOCK_ADAPTER.openDoor(block); } /** * Closes the door. */ public void close() { DungeonsXL.BLOCK_ADAPTER.closeDoor(block); } @Override public String toString() { return getClass().getSimpleName() + "{block=" + block + "; attachedBlock=" + attachedBlock + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/MultiBlock.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import org.bukkit.block.Block; /** * In some cases, a "game block" might actually be a structure with multiple blocks, like a bed or a door with two halfs. These GameBlocks implement MultiBlock. * * @author Daniel Saukel */ public interface MultiBlock { Block getAttachedBlock(); } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/PlaceableBlock.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.trigger.SignTrigger; import de.erethon.dungeonsxl.world.DGameWorld; import de.erethon.xlib.item.ExItem; import de.erethon.xlib.util.BlockUtil; import de.erethon.xlib.util.NumberUtil; import java.util.HashSet; import java.util.Set; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; /** * @author Frank Baumann, Daniel Saukel */ public class PlaceableBlock extends GameBlock { private DGameWorld gameWorld; private Set materials = new HashSet<>(); private Set faces = new HashSet<>(); private int triggerId = -1; public PlaceableBlock(DungeonsAPI api, DGameWorld gameWorld, Block block, String ids, String args) { super(api, block); this.gameWorld = gameWorld; for (String id : ids.split(",")) { ExItem item = api.getXLib().getExItem(id); if (item != null) { materials.add(item); } } faces.add(BlockFace.SELF); for (String arg : args.split(",")) { int id = NumberUtil.parseInt(arg, -1); if (id != -1) { triggerId = id; } else { faces.add(BlockUtil.lettersToBlockFace(arg)); } } } /* Actions */ @Override public boolean onBreak(BlockBreakEvent event) { return false; } public void onPlace(Player player) { if (triggerId != -1) { SignTrigger.getById(triggerId, gameWorld).trigger(true, player); } gameWorld.removeGameBlock(this); } public boolean canPlace(Block toPlace, ExItem material) { return block.getX() == toPlace.getX() && block.getY() == toPlace.getY() && block.getZ() == toPlace.getZ() && faces.contains(toPlace.getFace(block)) && (materials.isEmpty() || materials.contains(material)); } public static boolean canBuildHere(Block block, ExItem material, DGameWorld gameWorld) { for (PlaceableBlock gamePlaceableBlock : gameWorld.getPlaceableBlocks()) { if (gamePlaceableBlock.canPlace(block, material)) { return true; } } return false; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/ProtectedBlock.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.api.DungeonsAPI; import org.bukkit.block.Block; import org.bukkit.event.block.BlockBreakEvent; /** * @author Daniel Saukel */ public class ProtectedBlock extends GameBlock { public ProtectedBlock(DungeonsAPI api, Block block) { super(api, block); } /* Actions */ @Override public boolean onBreak(BlockBreakEvent event) { return true; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/RewardChest.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.Reward; import de.erethon.dungeonsxl.api.dungeon.Game; import de.erethon.dungeonsxl.api.event.group.GroupCollectRewardEvent; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.api.player.PlayerGroup; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.reward.ItemReward; import de.erethon.dungeonsxl.reward.LevelReward; import de.erethon.dungeonsxl.reward.MoneyReward; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import de.erethon.xlib.util.SimpleDateUtil; import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.inventory.ItemStack; /** * @author Frank Baumann, Daniel Saukel */ public class RewardChest extends GameBlock { private Economy econ; private boolean used = false; private double moneyReward; private int levelReward; private ItemStack[] itemReward; public RewardChest(DungeonsXL plugin, Block container, double moneyReward, int levelReward, ItemStack[] itemReward) { super(plugin, container); econ = plugin.getXLib().getEconomyProvider(); this.moneyReward = moneyReward; this.levelReward = levelReward; this.itemReward = itemReward; } /** * @return if the RewardChest is used */ public boolean isUsed() { return used; } /** * @param used set if the chest is used */ public void setUsed(boolean used) { this.used = used; } /** * @return the moneyReward */ public double getMoneyReward() { return moneyReward; } /** * @param moneyReward the moneyReward to set */ public void setMoneyReward(double moneyReward) { this.moneyReward = moneyReward; } /** * @return the levelReward */ public double getLevelReward() { return levelReward; } /** * @param levelReward the levelReward to set */ public void setLevelReward(int levelReward) { this.levelReward = levelReward; } /* Actions */ @Override public boolean onBreak(BlockBreakEvent event) { return true; } /** * @param opener the player who opens the chest */ public void onOpen(Player opener) { if (used) { MessageUtil.sendMessage(Bukkit.getPlayer(opener.getUniqueId()), DMessage.ERROR_CHEST_IS_OPENED.getMessage()); return; } if (block.getLocation().distance(block.getLocation()) < 1) { addTreasure(api.getPlayerGroup(opener), api.getPlayerCache().getGamePlayer(opener)); used = true; } } public void addTreasure(PlayerGroup group, GamePlayer collector) { if (group == null) { return; } group.sendMessage(DMessage.GROUP_REWARD_CHEST.getMessage()); boolean hasMoneyReward = false; boolean hasLevelReward = false; boolean hasItemReward = false; for (Reward reward : group.getRewards()) { if (reward instanceof MoneyReward) { hasMoneyReward = true; GroupCollectRewardEvent event = new GroupCollectRewardEvent(group, collector, reward); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { ((MoneyReward) reward).addMoney(moneyReward); } } else if (reward instanceof LevelReward) { hasLevelReward = true; GroupCollectRewardEvent event = new GroupCollectRewardEvent(group, collector, reward); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { ((LevelReward) reward).addLevels(levelReward); } } else if (reward instanceof ItemReward) { hasItemReward = true; GroupCollectRewardEvent event = new GroupCollectRewardEvent(group, collector, reward); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { ((ItemReward) reward).addItems(itemReward); } } } Game game = group.getGame(); if (game == null || game.hasRewards()) { if (!hasMoneyReward) { MoneyReward reward = new MoneyReward(econ); reward.addMoney(moneyReward); GroupCollectRewardEvent event = new GroupCollectRewardEvent(group, collector, reward); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { group.getRewards().add(reward); } } if (!hasLevelReward) { LevelReward reward = new LevelReward(); reward.addLevels(levelReward); GroupCollectRewardEvent event = new GroupCollectRewardEvent(group, collector, reward); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { group.getRewards().add(reward); } } if (!hasItemReward) { ItemReward reward = new ItemReward(api); reward.addItems(itemReward); GroupCollectRewardEvent event = new GroupCollectRewardEvent(group, collector, reward); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { group.getRewards().add(reward); } } } for (Player player : group.getMembers().getOnlinePlayers()) { DGamePlayer dPlayer = (DGamePlayer) api.getPlayerCache().getGamePlayer(player); if (!dPlayer.canLoot(game.getDungeon())) { MessageUtil.sendMessage(player, DMessage.ERROR_NO_REWARDS_TIME.getMessage(SimpleDateUtil.ddMMyyyyhhmm(dPlayer.getTimeNextLoot(game.getDungeon())))); continue; } if (itemReward != null) { String msg = ""; for (ItemStack itemStack : itemReward) { if (itemStack == null) { continue; } String name = null; if (itemStack.hasItemMeta()) { if (itemStack.getItemMeta().hasDisplayName()) { name = itemStack.getItemMeta().getDisplayName(); } } if (name == null) { name = VanillaItem.get(itemStack.getType()).getName(); } msg += ChatColor.RED + " " + itemStack.getAmount() + " " + name + ChatColor.GOLD + ","; } if (msg.length() >= 1) { msg = msg.substring(0, msg.length() - 1); } MessageUtil.sendMessage(player, DMessage.PLAYER_LOOT_ADDED.getMessage(msg)); } if (moneyReward != 0 && econ != null) { MessageUtil.sendMessage(player, DMessage.PLAYER_LOOT_ADDED.getMessage(econ.format(moneyReward))); } if (levelReward != 0) { MessageUtil.sendMessage(player, DMessage.PLAYER_LOOT_ADDED.getMessage(levelReward + " levels")); } } } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/TeamBed.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGamePlayer; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.xlib.category.Category; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; /** * @author Daniel Saukel */ public class TeamBed extends TeamBlock implements MultiBlock { private Block attachedBlock; public TeamBed(DungeonsAPI api, Block block, DGroup owner) { super(api, block, owner); attachedBlock = getAttachedBlock(); } /* Getters and setters */ public Block getAttachedBlock(Block block) { if (Category.BEDS.containsBlock(block.getRelative(BlockFace.EAST))) { return block.getRelative(BlockFace.EAST); } else if (Category.BEDS.containsBlock(block.getRelative(BlockFace.NORTH))) { return block.getRelative(BlockFace.NORTH); } else if (Category.BEDS.containsBlock(block.getRelative(BlockFace.WEST))) { return block.getRelative(BlockFace.WEST); } else if (Category.BEDS.containsBlock(block.getRelative(BlockFace.SOUTH))) { return block.getRelative(BlockFace.SOUTH); } else { return null; } } @Override public Block getAttachedBlock() { if (attachedBlock != null) { return attachedBlock; } else { return getAttachedBlock(block); } } /* Actions */ @Override public boolean onBreak(BlockBreakEvent event) { Player breaker = event.getPlayer(); if (owner.getMembers().contains(breaker)) { MessageUtil.sendMessage(breaker, DMessage.ERROR_BLOCK_OWN_TEAM.getMessage()); return true; } for (DGamePlayer player : owner.getDGamePlayers()) { player.setLives(1); } owner.setLives(0); owner.getGameWorld().sendMessage(DMessage.GROUP_BED_DESTROYED.getMessage(owner.getName(), api.getPlayerCache().getGamePlayer(breaker).getName())); Block block1 = event.getBlock(); if (DungeonsXL.BLOCK_ADAPTER.isBedHead(block)) { Block block2 = getAttachedBlock(block1); if (block2 != null) { block2.setType(VanillaItem.AIR.getMaterial()); } } block1.setType(VanillaItem.AIR.getMaterial()); return true; } @Override public String toString() { return getClass().getSimpleName() + "{block=" + block + "; attachedBlock=" + attachedBlock + "; owner=" + owner + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/TeamBlock.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.player.DGroup; import org.bukkit.block.Block; /** * @author Daniel Saukel */ public abstract class TeamBlock extends GameBlock { protected DGroup owner; public TeamBlock(DungeonsAPI api, Block block, DGroup owner) { super(api, block); this.owner = owner; } /* Getters and setters */ /** * @return the group that owns the flag */ public DGroup getOwner() { return owner; } /** * @param owner the owner group to set */ public void setOwner(DGroup owner) { this.owner = owner; } @Override public String toString() { return getClass().getSimpleName() + "{block=" + block + "; owner=" + owner + "}"; } } ================================================ FILE: core/src/main/java/de/erethon/dungeonsxl/world/block/TeamFlag.java ================================================ /* * Copyright (C) 2012-2013 Frank Baumann; 2015-2026 Daniel Saukel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package de.erethon.dungeonsxl.world.block; import de.erethon.dungeonsxl.DungeonsXL; import de.erethon.dungeonsxl.api.DungeonsAPI; import de.erethon.dungeonsxl.api.player.GamePlayer; import de.erethon.dungeonsxl.config.DMessage; import de.erethon.dungeonsxl.player.DGroup; import de.erethon.xlib.chat.MessageUtil; import de.erethon.xlib.item.VanillaItem; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.block.BlockBreakEvent; /** * @author Daniel Saukel */ public class TeamFlag extends TeamBlock { public TeamFlag(DungeonsAPI api, Block block, DGroup owner) { super(api, block, owner); reset(); } /* Actions */ /** * Reset a team flag when the capturer dies. */ public void reset() { DungeonsXL.BLOCK_ADAPTER.setBlockWoolColor(block, owner.getDColor()); } @Override public boolean onBreak(BlockBreakEvent event) { Player breaker = event.getPlayer(); GamePlayer gamePlayer = api.getPlayerCache().getGamePlayer(breaker); if (gamePlayer == null) { return true; } if (owner.getMembers().contains(breaker)) { MessageUtil.sendMessage(breaker, DMessage.ERROR_BLOCK_OWN_TEAM.getMessage()); return true; } owner.getGameWorld().sendMessage(DMessage.GROUP_FLAG_STEALING.getMessage(gamePlayer.getName(), owner.getName())); gamePlayer.setRobbedGroup(owner); event.getBlock().setType(VanillaItem.AIR.getMaterial()); return true; } } ================================================ FILE: core/src/main/resources/languages/english.yml ================================================ announcer: click: "&4&l=> &6CLICK HERE TO JOIN &4&l<=" button: accept: "&a[ YES ]" deny: "&4[ NO ]" okay: "&a[ OK ]" cmd: announce: help: "/dxl announce [name] - Runs an announcement script" break: breakMode: "&6You may break a block protected by DungeonsXL." help: "/dxl break - Break a block protected by DungeonsXL" protectedMode: "&6You may not break blocks protected by DungeonsXL anymore." chat: dungeonChat: "&6You have entered the dungeon chat" help: "/dxl chat - Change the chat mode" normalChat: "&6You are now in the public chat" chatspy: help: "/dxl chatspy - Dis/enables the spymode" started: "&6You started to spy the dungeon chat." stopped: "&6You stopped to spy the dungeon chat." create: help: "/dxl create [name] - Creates a new dungeon map" delete: backups: "&6Do you wish to delete all saved backups as well?" help: "/dxl delete [name] - Deletes a dungeon map" success: "&6Successfully deleted the map &4&v1&6." dungeonItem: dungeonItemHelp: "&6After finishing a game, &4dungeon items &6are removed from the player's inventory even if the respective dungeon setup in general allows to keep it." globalItemHelp: "&6After finishing a game, &4global items &6can be taken out of dungeons if the respective dungeon setup in general allows to keep the inventory." help: "/dxl dungeonItem [true|false|info] - Sets the item stack in the player's hand to be one that cannot be taken out of a dungeon" info: dungeon: "&6This item is a &4dungeon item&6." global: "&6This item is a &4global item&6." set: dungeon: "&6Successfully made this item a &4dungeon item&6." global: "&6Successfully made this item a &4global item&6." edit: help: "/dxl edit [map] - Edit an existing dungeon map" enter: help: "/dxl enter ([joining group]) [target group] - Let the joining group enter the game of the target group" success: "&6The group &4&v1 &6successfully entered the game of the group &4&v2&6." escape: help: "/dxl escape - Leaves the current edit world without saving" game: help: "/dxl game - Shows information about the current game session" group: help: create: "/dxl group create [group] - Creates a new group" disband: "/dxl group disband ([group]) - Disbands a group" invite: "/dxl group invite [player] - Invites someone to your group" join: "/dxl group join [group]- Join a group" kick: "/dxl group kick [player] - Kicks a player" main: "/dxl group - Shows group command help" show: "/dxl group show [group] - Shows a group" uninvite: "/dxl group uninvite [player] - Takes back an invitation to your group" help: help: "/dxl help [page] - Shows the help page" import: help: "/dxl import [world] - Imports a world from the world container as a dungeon map" success: "&6Successfully imported the world &4&v1&6." invite: help: "/dxl invite [player] [dungeon] - Invite a player to edit a dungeon" success: "&6Player &4&v1&6 was successfully invited to edit the map &4&v2&6." join: help: "/dxl join [announcement] - Opens the GUI to join a group in an upcoming game" kick: help: "/dxl kick [player] - Kicks the player out of his group and dungeon" success: "&6Successfully kicked &4&v1&6 out of the group." leave: help: "/dxl leave - Leaves the current group and dungeon or edit world" success: "&6You have successfully left your group." list: help: "/dxl list ([dungeon|map|loaded]) ([dungeon]) - Lists all dungeons" lives: group: "&4&v1 &6have &4&v2 &6lives left." help: "/dxl lives [player] - Shows the lives a player has left" player: "&4&v1 &6has &4&v2 &6lives left." main: compatibility: "&eInternals: &o[&v1] &eVault: &o[&v2] &eXLib: &o[&v3]" help: "/dxl - General status information" helpInfo: "&7Type in &o/dxl help&r&7 for further information." loaded: "&eMaps: &o[&v1] &eDungeons: &o[&v2] &eLoaded: &o[&v3] &ePlayers: &o[&v4]" welcome: "&7Welcome to &4Dungeons&fXL" msg: added: "&6New Messages (&4&v1&6) added." help: "/dxl msg [id] '[msg]' - Show or edit a message" updated: "&6Messages (&4&v1&6) updated." play: help: "/dxl play [name] - Allows the player to play a dungeon without a portal" portal: help: "/dxl portal ([material=portal]) - Creates a portal that leads into a dungeon" reload: fail: "&4You cannot reload right now." help: "/dxl reload - Reloads the plugin" players: "&4Warning: If you reload the plugin, all players will be kicked out of their game." success: "&7Successfully reloaded DungeonsXL." rename: help: "/dxl rename [old name] [new name] - Changes the name of a map to the new one. This command does NOT break dungeons that include this map." success: "&6Successfully renamed the map &4&v1&6 to &4&v2&6." resourcePack: help: "/dxl resourcepack [ID] - Downloads a resourcepack registered in the main configuration file; use 'reset' to reset" save: help: "/dxl save - Saves the current dungeon" success: "&6Map saved." status: help: "/dxl status - Shows the technical status of DungeonsXL" test: help: "/dxl test [name] - Starts the game in test mode" uninvite: help: "/dxl uninvite [player] [map] - Uninvite a player from editing a map" success: "&4&v1&6's permission to edit the map &4&v2&6 has been removed successfully." dayOfWeek: 0: "Sunday" 1: "Monday" 2: "Tuesday" 3: "Wednesday" 4: "Thursday" 5: "Friday" 6: "Saturday" error: bed: "&4You cannot use a bed while in a dungeon." blockOwnTeam: "&4This block belongs to your own group." chestIsOpened: "&4This chest has already been opened." cmd: "&4Commands are not allowed while in a dungeon." dispenser: "&4You cannot access this dispenser." drop: "&4You cannot drop safe items" enderchest: "&4You cannot use an enderchest while in a dungeon." groupIsPlaying: "&4This group is already in a dungeon." inGroup: "&4The player &6&v1&4 is already member of a group." joinGroup: "&4You have to join a group first." leaveDungeon: "&4You have to leave your current dungeon first." leaveGame: "&4You have to leave your current game first." leaveGroup: "&4You have to leave your group first." msgFormat: "&4Please use &6\" &4to mark the beginning and the end of the message." msgIdDoesNotExist: "&4Messages with Id &6&v1&4 does not exist." msgNoInt: "&4The argument [id] has to include a number." nameInUse: "&4The name &6&v1 &4is already in use." nameTooLong: "&4The name may not be longer than 15 characters." noGame: "&4You currently do not take part in a game." noItemInMainHand: "&4You do not have an item in your main hand." noLeaveInTutorial: "&4You cannot use this command in the tutorial." noPermissions: "&4You do not have permission to do this." noProtectedBlock: "&4This is not a block protected by DungeonsXL." noReadySign: "&4The world &6&v1 &4does not seem to have a ready sign. No game can be started and only lobby dungeon signs will be initialized." noRewardsTime: "&4You cannot receive rewards before &6&v1&4." noSuchAnnouncement: "&4This announcement does not exist." noSuchDungeon: "&4This dungeon does not exist." noSuchGroup: "&4The group &6&v1&4 does not exist." noSuchMap: "&4The world &6&v1&4 does not exist." noSuchPlayer: "&4The player &6&v1&4 does not exist." noSuchResourcePack: "&4The resource pack &6&v1 &4is not registered in the main configuration file." noSuchShop: "&4Shop &v1 &4not found..." notInDungeon: "&4You are not in a dungeon." notInGame: "&4The group &6&v1&4 is not member of a game." notInGroup: "&4The player &6&v1&4 is not member of the group &6&v2&v4." notInvited: "&4You are not invited to the group &6&v1&4." notLeader: "&4You are not the captain of your group." notSaved: "&4The map &6&v1&4 has not been saved to the &6DungeonsXL/maps/ &4folder yet." ready: "&4Choose your class first." requirements: "&4You don't fulfill the requirements for this dungeon." selfNotInGroup: "&4You are not in any group." signWrongFormat: "&4The sign is not written correctly." tooManyInstances: "&4There are currently too many maps instantiated. Try it again in a few minutes." tooManyTutorials: "&4There are currently too many tutorials running. Try it again in a few minutes." tutorialDoesNotExist: "&4Tutorial dungeon does not exist." group: bedDestroyed: "&6The bed of the group &4&v1 &6has been destroyed by &4&v2&6." congrats: "&6Congratulations!" congratsSub: "&l&4Your group &v1 &4won the match." created: "&4&v1&6 created the group &4&v2&6." death: "&4&v1 &6died. &4&v2 &6have &4&v3 &6lives left." deathKick: "&4&v1 &6was kicked because &4&v2 &6have no lives left." defeated: "&4The group &4v1 &6is defeated because it lost its last score point." disbanded: "&4&v1&6 disbanded the group &4&v2&6." flagCaptured: "&4&v1&6 has captured the flag of the group &4&v2&6." flagLost: "&4&v1&6 died and lost &4&v2&6's flag." flagStealing: "&4&v1&6 is stealing the flag of the group &4&v2&6." invitedPlayer: "&4&v1&6 invited the player &4&v2&6 to the group &4&v3&6." joinedGame: "&6Your group successfully joined the game." kickedPlayer: "&4&v1&6 kicked the player &4&v2&6 from the group &4&v3&6." killed: "&4&v1 &6was killed by &4&v2&6. &4&v3&6 have &4&v4 &6lives left." killedKick: "&4&v1&6 was killed by &4&v2&6. &4&v3 have no lives left." livesAdded: "&6Your group received a bonus of &4&v1&6 lives." livesRemoved: "&6Your group lost &4&v1&6 lives." playerJoined: "&6Player &4&v1&6 has joined the group." rewardChest: "&6Your group has found a reward chest." uninvitedPlayer: "&4&v1&6 took back the invitation for &4&v2&6 to the group &4&v3&6." waveFinished: "&6Your group finished wave no. &4&v1&6. The next one is going to start in &4&v2&6 seconds." player: blockInfo: "&6Block ID: &2&v1" checkpointReached: "&6Checkpoint reached." death: "&4&v1 &6died and has &4&v2 &6lives left." deathKick: "&2&v1 &6lost his last life and was kicked." finishedDungeon: "&6You successfully finished the dungeon." finished_Floor: "&6You successfully finished the floor." invited: "&4&v1&6 invited you to the group &4&v2&6." joinGroup: "&6You successfully joined the group." kicked: "&4You have been kicked out of the group &6&v1&4." killed: "&4&v1 &6was killed by &4&v2 &6and has &4&v3 &6lives left." killedKick: "&4&v1&6 was killed by &4&v2 &6and lost his last life." leaveGroup: "&6You have successfully left your group." leftGroup: "&6Player &4&v1&6 has left the Group." livesAdded: "&6Received a bonus of &4&v1&6 lives." livesRemoved: "&6You lost &4&v1&6 lives." lootAdded: "&4&v1&6 have been added to your reward inventory." newLeader: "&6You are now the new captain of your group." offline: "&6Player &4&v1&6 went offline. In &4&v2&6 seconds he will autmatically be kicked from the dungeon." offlineNever: "&6The player &4&v1&6 went offline. He will &4not&6 be kicked from the dungeon automatically." portal: abort: "&6Portal creation cancelled." created: "&6Portal created." introduction: "&6Click on the two edges of the portal with the wooden sword." progress: "&6First edge successfully marked. You may now click on the other edge." rotate: "&6Do you wish to rotate the portal?" protectedBlockDeleted: "&6Successfully removed the protection." ready: "&6You are now ready to start the dungeon." signCopied: "&6Sign data copied." signCreated: "&6Successfully created a dungeon sign." timeKick: "&2&v1&6's time expired." timeLeft: "&v1You have &6&v2 &v1seconds left to finish the dungeon." treasures: "&1Treasures" uninvited: "&4&v1&6 took back your invitation to the group &4&v2&6." unlimitedLives: "unlimited" waitForOtherPlayers: "&6Waiting for team members..." requirement: fee: "&6You have been charged &4&v1 &6for entering the dungeon." feeItems: "Items" feeLevel: "Levels" feeMoney: "Money" finishedDungeons: and: "and" name: "Finished dungeons" or: "or" withinTime: "within the last &v1 hours" forbiddenItems: "Forbidden items" groupSize: "Group size" keyItems: "Keys" permission: "Permissions" timeSince: finish: "Enough passed time since last playthrough (&v1 hours)" start: "Enough passed time since last try (&v1 hours)" never: "No playthroughs so far." timeframe: "Timeframe" reward: general: "&6You received &4&v1 &6for finishing the dungeon." sign: end: "&aEND" floor: 1: "&aENTER" 2: "&aNEXT FLOOR" global: full: "&4FULL" isPlaying: "&4IS PLAYING" joinGame: "&aJOIN GAME" joinGroup: "&aJOIN GROUP" newGame: "&aNEW GAME" newGroup: "&aNEW GROUP" leave: "&aLEAVE" ready: "&aREADY" resourcePack: "&aDOWNLOAD" wave: 1: "&aSTART" 2: "&aNEXT WAVE" ================================================ FILE: core/src/main/resources/languages/french.yml ================================================ announcer: click: "&4&l=> &6CLIQUEZ ICI POUR REJOINDRE &4&l<=" button: accept: "&a[ OUI ]" deny: "&4[ NON ]" okay: "&a[ OK ]" cmd: announce: help: "/dxl announce [name] - Exécuter un script d'annonce" break: breakMode: "&6Vous pouvez casser un bloc protégé par DungeonsXL." help: "/dxl break - Casser un bloc protégé par DungeonsXL" protectedMode: "&6Vous ne pouvez plus casser des blocs protégés par DungeonsXL." chat: dungeonChat: "&6Vous êtes entrés dans l'espace de discussion du donjon." help: "/dxl chat - Changer le mode de discussion" normalChat: "&6Vous êtes maintenant dans l'espace de discussion public." chatspy: help: "/dxl chatspy - Activer/désactiver le mode espionnage" started: "&6Vous avez commencé à espionner l'espace de discussion du donjon." stopped: "&6Vous avez arrêté d'espionner l'espace de discussion du donjon." create: help: "/dxl create [name] - Créer une nouvelle carte de donjon" delete: backups: "&6Voulez-vous vraiment supprimer toutes les sauvegardes ?" help: "/dxl create [name] - créé une nouvelle carte donjon" success: "&6 &4&v1&6 a été supprimé avec succès." dungeonItem: dungeonItemHelp: "&6Après avoir fini une partie, &4les objets du donjons &6sont supprimés de l'inventaire du joueur sauf si les paramètres du donjons autorisent à les conserver." globalItemHelp: "&6Après avoir fini une partie, &4les objets globaux &6peuvent être sortis du donjon si les paramètres du donjon l'autorise." help: "/dxl dungeonItem [true|false|info] - Défini la pile d'objet dans les mains du joueur comme ne pouvant être sortie du donjon." info: dungeon: "&6C'est un &4objet de donjon&6." global: "&6C'est un &4objet global&6." set: dungeon: "&6Vous avez réussi à en faire un &4objet de donjon&6." global: "&6Vous avez réussi à en faire un &4objet global&6." edit: help: "/dxl edit [name] - Editer une carte existante de donjon" enter: help: "/dxl enter ([joining group]) [target group] - Laisser le groupe en train de rejoindre, entrer dans la partie du groupe cible" success: "&6Le groupe &4&v1 &6est rentré avec succès dans la partie du groupe &4&v2&6." escape: help: "/dxl escape - Quitter le monde actuel en cours d'édition sans sauvegarder" game: help: "/dxl game - Montrer les informations à propos de la partie de jeu actuel" group: help: create: "/dxl group create [group] - Créer un nouveau groupe" disband: "/dxl group disband ([group]) - Dissoudre un groupe" invite: "/dxl group invite [player] - Inviter quelqu'un a votre groupe" join: "/dxl group join [group]- Rejoindre un groupe" kick: "/dxl group kick [player] - Éjecter un joueur" main: "/dxl group - Montrer l'aide des commandes de groupe" show: "/dxl group show [group] - Montrer un groupe" uninvite: "/dxl group uninvite [player] - Retirer une invitation à votre groupe" help: help: "/dxl help [page] - Montrer la page d''aide" import: help: "/dxl import [world] - Importe un monde du conteneur de monde en tant que carte de donjon" success: "&6Importation du monde &4&v1&6 avec succès." invite: help: "/dxl invite [player] [dungeon] - Inviter un joueur à éditer un donjon" success: "&6Le joueur &4&v1&6 a été invité avec succès à éditer la carte &4&v2&6." join: help: "/dxl join [announcement] - Ouvrir l'interface pour rejoindre un groupe dans une prochaine partie" kick: help: "/dxl kick [player] - Éjecte le joueur hors de son groupe et du donjon en cours" success: "&6Éjection de &4&v1&6 du groupe réussite." leave: help: "/dxl leave - Quitter le groupe et le donjon ou le monde en cours d''édition" success: "&6Vous avez quitté le groupe avec succès." list: help: "/dxl list ([dungeon|map|loaded]) ([dungeon]) - Lister tous les donjons" lives: group: "&4&v1&6 ont &4&v2 &6vies restantes." help: "/dxl lives [player] - Montrer les vies d''un joueur venant de partir" player: "&4&v1&6 a &4&v2 &6vies restantes." main: compatibility: "&eInternals:&o[&v1] &eVault:&o[&v2] &eXLib:&o[&v3]" help: "/dxl - Informations générales" helpInfo: "&7Tapez dans &o/dxl help&r&7 pour plus d'information." loaded: "&eCartes:&o[&v1] &eDonjons:&o[&v2] &eChargés:&o[&v3] &eJoueurs:&o[&v4]" welcome: "&7Bienvenue sur &4Dungeons&fXL" msg: added: "&6Nouveaux messages (&4&v1&6) ajoutés." help: "/dxl msg [id] '[msg]' - Montrer ou éditer un message" updated: "&6Messages (&4&v1&6) mis à jour." play: help: "/dxl play [name] - Permettre au joueur de jouer à un donjon sans un portail" portal: help: "/dxl portal ([material=portal]) - Créer un portail qui mène à un donjon" reload: fail: "&4Vous ne pouvez pas recharger maintenant." help: "/dxl reload - Recharge le plugin" players: "&4Attention: Si vous rechargez le plugin, tous les joueurs seront éjectés de leur jeu." success: "&7DungeonsXL a été rechargé avec succès." rename: help: "/dxl rename [old name] [new name] - Change le nom de la carte en un nouveau. Cette commande NE casse PAS les donjons comportant cette carte." success: "&6Carte &4&v1&6 renommée en &4&v2&6 avec succès." resourcePack: help: "/dxl resourcepack [ID] - Télécharge un pack de ressources enregistré dans le fichier configuration principale; utilisez 'reset' pour réinitialisé" save: help: "/dxl save - Sauvegarder le donjon actuel" success: "&6Carte sauvegardée." status: help: "/dxl status - Affiche le status technique de DungeonsXL" test: help: "/dxl test [name] - Lance une partie en mode test" uninvite: help: "/dxl uninvite [player] [dungeon] - Désinvite un joueur à éditer un donjon" success: "&4&v1&6 a été désinviter avec succès à éditer la carte &4&v1&6." dayOfWeek: 0: "Dimanche" 1: "Lundi" 2: "Mardi" 3: "Mercredi" 4: "Jeudi" 5: "Vendredi" 6: "Samedi" error: bed: "&4Vous ne pouvez utiliser un lit en étant dans un donjon." blockOwnTeam: "&4Ce bloque appartient à votre groupe." chestIsOpened: "&4Ce coffre a déjà été ouvert." cmd: "&4Les commandes ne sont pas autorisées pendant un donjon." dispenser: "&4Vous ne pouvez accéder à ce distributeur." drop: "&4Vous ne pouvez pas lâcher cet objet protégé." enderchest: "&4Vous ne pouvez utiliser un coffre de l'ender quand vous êtes dans un donjon." groupIsPlaying: "&4Ce groupe est déjà dans un donjon." inGroup: "&4Le joueur &6&v1&4 est déjà membre d'un groupe." joinGroup: "&4Vous devez d'abord rejoindre un groupe." leaveDungeon: "&4Vous devez d'abord quitter votre donjon actuel." leaveGame: "&4Vous devez d'abord quitter votre partie actuelle." leaveGroup: "&4Vous devez d'abord quitter votre groupe." msgFormat: "&4Les messages doivent être entre \"." msgIdDoesNotExist: "&4Le message avec l'Id &6&v1&4 n'existe pas." msgNoInt: "&4L'argument [id] doit inclure un nombre." nameInUse: "&4Le nom &6&v1 &4est déjà utilisé." nameTooLong: "&4Le nom ne devrait pas être plus long que 15 caractères." noGame: "&4Vous ne prenez actuellement pas part à une partie." noItemInMainHand: "&4Vous n'avez pas d'objet dans votre main principale." noLeaveInTutorial: "&4Vous ne pouvez utiliser cette commande dans le tutoriel." noPermissions: "&4Vous n'avez pas la permission de faire ça." noProtectedBlock: "&4Ce n'est pas un bloc protégé par DungeonsXL." noReadySign: "&4La carte &6&v1 &4n'a pas de panneau \"ready\". On ne peut pas commencer des jeus et les panneaux de lobby ne sont que initialisés." noRewardsTime: "&4Vous ne pouvez pas recevoir de récompense avant &6&v1&4." noSuchAnnouncement: "&4Cette annonce n'existe pas." noSuchDungeon: "&4Le donjon &6&v1&4 n'existe pas." noSuchGroup: "&4Le groupe &6&v1&4 n'existe pas." noSuchMap: "&4La carte &6&v1&4 n'existe pas." noSuchPlayer: "&4Le joueur &6&v1&4 n'existe pas." noSuchResourcePack: "&4Le paque de ressources &6&v1 &4n'est pas enregistré dans le fichier de configuration principal." noSuchShop: "&4Shop &v1 &4not found..." notInDungeon: "&4Vous n'êtes pas dans un donjon." notInGame: "&4Le groupe &6&v1&4 n'est pas membre d'une partie." notInGroup: "&4Le joueur &6&v1&4 n'est pas membre du groupe &6&v2&v4." notInvited: "&4Vous n'êtes pas invité au groupe &6&v1&4." notLeader: "&4Vous n'êtes pas le capitaine de votre groupe." notSaved: "&4La carte &6&v1&4 n'a pas encore été sauvegardée dans le dossier &6DungeonsXL/maps/&4." ready: "&4Choisissez d'abord votre classe." requirements: "&4Vous ne remplissez pas les prérequis pour ce donjon." selfNotInGroup: "Vous n'êtes dans aucun groupe." signWrongFormat: "&4Le panneau n'est pas rédigé correctement." tooManyInstances: "&4Il y a actuellement trop de carte instanciées. Réessayez dans quelques minutes." tooManyTutorials: "&4Il y a actuellement trop de tutoriel en cours. Réessayez dans quelques minutes." tutorialDoesNotExist: "&4Le tutoriel du donjon n'existe pas." group: bedDestroyed: "&6Le lit du groupe &4&v1 &6a été détruit par &4&v2&6." congrats: "&6Félicitations !" congratsSub: "&l&4Votre groupe &v1 &4gagne le match." created: "&4&v1&6 a créé le groupe &4&v2&6." death: "&4&v1 &6est mort. &4&v2 &6a &4&v3 &6vies restantes." deathKick: "&4&v1 &6a été éjecté car &4&v2 &6n'a plus de vie restante." defeated: "&4Le groupe &4v1 &6a été défait car il a perdu son dernier point de score." disbanded: "&4&v1&6 a dissous le groupe &4&v2&6." flagCaptured: "&4&v1&6 a capturé le drapeau du groupe &4&v2&6." flagLost: "&4&v1&6 est mort et a perdu le drapeau de &4&v2&6." flagStealing: "&4&v1&6 is stealing the flag of the group &4&v2&6." invitedPlayer: "&4&v1&6 a invité le joueur &4&v2&6 au groupe &4&v3&6." joinedGame: "&6Votre groupe a rejoint la partie avec succès." kickedPlayer: "&4&v1&6 a éjecté &4&v2&6 du groupe &4&v3&6." killed: "&4&v1 &6a été tué par &4&v2&6. &4&v3&6 a &4&v4 &6vies restantes." killedKick: "&4&v1&6 a été tué par &4&v2&6. &4&v3 n'a plus de vies restantes." livesAdded: "&6Votre groupe a reçu un bonus de &4&v1&6 vies." livesRemoved: "&6Votre groupe a perdu &4&v1&6 vies." playerJoined: "&6Le joueur &4&v1&6 a rejoint le groupe." rewardChest: "&6Votre groupe a trouvé un coffre de récompenses." uninvitedPlayer: "&4&v1&6 a repris l'invitation de &4&v2&6 pour le groupe &4&v3&6." waveFinished: "&6Votre groupe a fini la vague numéro &4&v1&6. La suivante va commencer dans &4&v2&6 secondes." player: blockInfo: "&6Block ID:&2&v1" checkpointReached: "&6Point de sauvegarde atteint." death: "&4&v1 &6est mort, vies restantes:&4&v2" deathKick: "&2&v1&6 est mort et a perdu sa dernière vie." finishedDungeon: "&6Vous avez terminé le donjon avec succès." finished_Floor: "&6Vous avez terminé l'étage avec succès." invited: "&4&v1&6 vous a invité au groupe &4&v2&6." joinGroup: "&6Vous avez rejoint le groupe avec succès." kicked: "&4Vous avez été éjecté du groupe &6&v1&4." killed: "&4&v1 &6a été tué par &4&v2 &6et a &4&v3 &6vies restantes." killedKick: "&4&v1&6 a été tué par &4&v2 &6et a perdu sa dernière vie." leaveGroup: "&6Vous avez quitté le groupe avec succès." leftGroup: "&6Le joueur &4&v1&6 a quitté le groupe." livesAdded: "&6Received a bonus of &4&v1&6 lives." livesRemoved: "&6Vous perdez &4&v1&6 vies." lootAdded: "&4&v1&6 a été rajouté à votre inventaire de récompenses." newLeader: "&6Vous êtes maintenant le nouveau capitaine du groupe." offline: "&6Le joueur &4&v1&6 est passé hors ligne. Dans &4&v2&6 secondes il va être automatiquement éjecté du donjon." offlineNever: "&6Le joueur &4&v1&6 est passé hors ligne. Il ne va &4pas&6 être éjecté du donjon automatiquement." portal: abort: "&6Création de portails annulée." created: "&6Portail créé." introduction: "&6Cliquez sur les deux bords du portail avec l'épée en bois." progress: "&6Le premier bord a été marqué avec succès. Vous devez maintenant cliquer sur le deuxième bord." rotate: "&6Voulez-vous tourner le portail ?" protectedBlockDeleted: "&6Protection retirée avec succès." ready: "&6Vous êtes maintenant prêt à démarrer le donjon." signCopied: "&6Données du panneau copiées." signCreated: "&6Création avec succès d'un panneau de donjon." timeKick: "Temps de &2&v1&6 écoulé." timeLeft: "&v1Il vous reste &6&v2 &v1secondes pour finir le donjon." treasures: "&1trésors" uninvited: "&4&v1&6 a annulé votre invitation au groupe &4&v2&6." unlimitedLives: "illimité" waitForOtherPlayers: "&6En attente des membres de l'équipe..." requirement: fee: "&6Vous avez été débité de &4&v1 &6pour entrer dans le donjon." feeItems: "Items" feeLevel: "Niveaux" feeMoney: "Argent" finishedDungeons: and: "et" name: "Donjons complétés" or: "ou" withinTime: "au cours des &v1 dernières heurs" forbiddenItems: "Objets défendus" groupSize: "Quantité des joueurs" keyItems: "Clés" permission: "Permissions" timeSince: finish: "Assez de temps passé depuis le dernier jeu terminé (&v1 heures)" start: "Assez de temps passé depuis le dernier jeu commencé (&v1 heures)" never: "Pas de jeux." timeframe: "Créneau horaire" reward: general: "&6Vous avez reçu &4&v1 &6pour avoir fini le donjon." sign: end: "&aFINIR" floor: 1: "&aENTRER AU PRO-" 2: "&aCHAIN ÉTAGE" global: full: "&4PLEIN" isPlaying: "&4EN JEU" joinGame: "&aINSCRIRE AU JEU" joinGroup: "&aINSCRIRE AU GROUPE" newGame: "&aNOUVEAU JEU" newGroup: "&aNOUVEAU GROUPE" leave: "&aQUITTER" ready: "&aPRÊT" resourcePack: "&aTÉLÉCHARGER" wave: 1: "&aCOMMENCER" 2: "&aVAGUE SUIVANTE" ================================================ FILE: core/src/main/resources/languages/german.yml ================================================ announcer: click: "&4&l=> &6KLICKE HIER ZUM BEITRETEN &4&l<=" button: accept: "&a[ JA ]" deny: "&4[ NEIN ]" okay: "&a[ OK ]" cmd: announce: help: "/dxl announce [name] - Startet ein Announcer-Script" break: breakMode: "&6Du darfst jetzt von DungeonsXL geschützte Blöcke zerstören." help: "/dxl break - Zerstöre einen von DungeonsXL geschützten Block" protectedMode: "&6Du darfst keine von DungeonsXL geschützten Blöcke mehr zerstören." chat: dungeonChat: "&6Du hast den Dungeon-Chat betreten." help: "/dxl chat - Chat-Modus wechseln" normalChat: "&6Du bist jetzt im öffentlichen Chat." chatspy: help: "/dxl chatspy - (De-)Aktiviert den Chat-Spy" started: "&6Du liest jetzt den Dungeon-Chat mit." stopped: "&6Du hast aufgehört, den Dungeon-Chat mitzulesen." create: help: "/dxl create [name] - Erstellt eine neue Dungeon-Map" delete: backups: "&6Möchtest Du auch alle gespeicherten Backups löschen?" help: "/dxl delete [name] - Löscht eine Dungeon-Map" success: "&6Die Welt &4&v1&6 wurde erfolgreich gelöscht." dungeonItem: dungeonItemHelp: "&6Nachdem ein Spiel beendet wurde, werden &4Dungeon-Items &6selbst dann aus dem Inventar entfernt, wenn die Spielregeln das eigentlich erlauben." globalItemHelp: "&6Nachdem ein Spiel beendet wurde, können &4globale Items &6aus den Dungeons mit herausgenommen werden, wenn die Spielregeln das erlauben." help: "/dxl dungeonItem [true|false|info] - Stellt den Item-Stack in der Hand des Spielers so ein, dass er nicht aus dem Dungeon mit herausgenommen werden kann" info: dungeon: "&6Dieses Item ist ein &4Dungeon-Item&6." global: "&6Dieses Item ist ein &4globales Item&6." set: dungeon: "&6Das Item wurde erfolgreich als &4Dungeon-Item&6 eingestellt." global: "&6Das Item wurde erfolgreich als &4globales Item&6 eingestellt." edit: help: "/dxl edit [map] - Eine vorhandene Welt bearbeiten" enter: help: "/dxl enter ([joining group]) [target group] - Lässt eine Gruppe das Spiel einer anderen betreten." success: "&6Die Gruppe &4&v1 &6ist erfolgreich dem Spiel der Gruppe &4&v2&6 beigetreten." escape: help: "/dxl escape - Die Welt ohne zu speichern verlassen" game: help: "/dxl game - Zeigt Informationen über das laufende Spiel an" group: help: create: "/dxl group create [group] - Erstellt eine neue Gruppe" disband: "/dxl group disband ([group]) - Löst eine Gruppe auf" invite: "/dxl group invite [player] - Lädt jemanden in die Gruppe ein" join: "/dxl group join [group]- Einer Gruppe beitreten" kick: "/dxl group kick [player] - Einen Spieler aus einer Gruppe herauswerfen" main: "/dxl group - Zeigt die Hilfeseiten des group-Befehls" show: "/dxl group show [group] - Zeigt eine Gruppe an" uninvite: "/dxl group uninvite [player] - Nimmt eine Einladung für die Gruppe zurück" help: help: "/dxl help [page] - Zeigt die Hilfeseite" import: help: "/dxl import [world] - Importiert eine Welt aus dem World-Container als Dungeon-Map" success: "&6Die Welt &4&v1&6 wurde erfolgreich importiert." invite: help: "/dxl invite [player] [dungeon] - Lädt einen Spieler ein, eine Dungeon-Welt zu bearbeiten" success: "&6Der Spieler &4&v1&6 wurde erfolgreich dazu eingeladen, die Welt &4&v2&6 zu bearbeiten." join: help: "/dxl join [announcement] - Öffnet ein GUI, um einer Gruppe in einem zukünftigen Spiel beizutreten" kick: help: "/dxl kick [player] - Wirft den Spieler aus seiner Gruppe und damit aus dem Dungeon heraus" success: "&4&v1&6 wurde erfolgreich aus der Gruppe geworfen." leave: help: "/dxl leave - Die Gruppe und Instanz verlassen" success: "&6Du hast Deine Gruppe erfolgreich verlassen." list: help: "/dxl list ([dungeon|map|loaded]) ([dungeon]) - Listet alle Dungeons auf" lives: group: "&4&v1 &6haben &4&v2 &6Leben übrig." help: "/dxl lives [player] - Zeigt die Leben, die ein Spieler übrig hat" player: "&4&v1 &6hat &4&v2 &6Leben übrig." main: compatibility: "&eInternals:&o[&v1] &eVault:&o[&v2] &eXLib:&o[&v3]" help: "/dxl - Grundlegende Statusinformationen" helpInfo: "&7Gib &o/dxl help&r&7 für weitere Informationen ein." loaded: "&eWelten:&o[&v1] &eDungeons:&o[&v2] &eGeladen:&o[&v3] &eSpieler:&o[&v4]" welcome: "&7Willkommen bei &4Dungeons&fXL" msg: added: "&6Neue Nachrichten (&4&v1&6) hinzugefügt." help: "/dxl msg [id] '[msg]' - Zeigt oder bearbeitet eine Nachricht" updated: "&6Nachrichten (&4&v1&6) aktualisiert." play: help: "/dxl play [name] - Erlaubt dem Spieler, einen Dungeon ohne Portal zu spielen" portal: help: "/dxl portal ([material=portal]) - Erstellt ein Portal, das in einen Dungeon führt" reload: fail: "&4Du kannst gerade nicht neu laden." help: "/dxl reload - Lädt das Plugin neu" players: "&4Warnung: Wenn Du das Plugin neu lädst, werden alle Spieler aus ihrem Spiel geworfen." success: "&7DungeonsXL wurde erfolgreich neu geladen." rename: help: "/dxl rename [old name] [new name] - Ändert den Namen des Dungeons in einen neuen. Dieser Befehl updatet auch Namensreferenzen zu dem Dungeon" success: "&4&v1&6 wurde erfolgreich zu &4&v2&6 umbenannt." resourcePack: help: "/dxl resourcepack [ID] - Downloadet ein in der Haupt-Config registriertes Ressourcenpaket; nutze 'reset' um es zurückzusetzen" save: help: "/dxl save - Speichert den Dungeon" success: "&6Welt gespeichert." status: help: "/dxl status - Zeigt den technischen Status von DungeonsXL" test: help: "/dxl test [name] - Startet das Spiel im Testmodus" uninvite: help: "/dxl uninvite [player] [map] - Lädt einen Spieler davon aus, eine Welt zu bearbeiten" success: "&4&v1&6's Berechtigung zum Bearbeiten der Welt &4&v2&6 wurde erfolgreich entfernt." dayOfWeek: 0: "Sonntag" 1: "Montag" 2: "Dienstag" 3: "Mittwoch" 4: "Donnerstag" 5: "Freitag" 6: "Samstag" error: bed: "&4Du kannst im Dungeon kein Bett benutzen." blockOwnTeam: "&4Dieser Block gehört zu Deiner eigenen Gruppe." chestIsOpened: "&4Diese Truhe wurde schon geöffnet." cmd: "&4Befehle sind im Dungeon nicht erlaubt." dispenser: "&4Du kannst auf diesen Werfer nicht zugreifen." drop: "&4Du kannst keine gesicherten Items wegwerfen." enderchest: "&4Du kannst im Dungeon keine Endertruhen benutzen." groupIsPlaying: "&4Diese Gruppe ist schon in einem Dungeon." inGroup: "&4Der Spieler &6&v1&4 ist schon Mitglied einer Gruppe." joinGroup: "&4Du musst zuerst einer Gruppe beitreten." leaveDungeon: "&4Du musst zuerst Deinen Dungeon verlassen." leaveGame: "&4Du musst zuerst Dein Spiel verlassen." leaveGroup: "&4Du musst zuerst Deine Gruppe verlassen." msgFormat: "&4Bitte benutze &6\"&4-Zeichen um den Anfang und das Ende der Nachricht zu markieren." msgIdDoesNotExist: "&4Eine Nachricht mit der ID &6&v1&4 gibt es nicht." msgNoInt: "&4Das Argument [id] muss numerisch sein." nameInUse: "&4Der Name &6&v1 &4wird bereits verwendet." nameTooLong: "&4Der Name darf nicht länger als 15 Zeichen sein." noGame: "&4Du nimmst momentan nicht an einem Spiel teil." noItemInMainHand: "&4Du hast kein Item in Deiner Haupthand." noLeaveInTutorial: "&4Du kannst diesen Befehl im Tutorial nicht benutzen." noPermissions: "&4Du hast nicht die nötigen Berechtigungen, um das zu tun." noProtectedBlock: "&4Das ist kein von DungeonsXL geschützter Block." noReadySign: "&4Die Welt &6&v1 &4hat kein Ready-Schild. Spiele können nicht begonnen werden und nur Lobby-Schilder werden geladen." noRewardsTime: "&4Du kannst vor &6&v1&4 keine Belohnungen bekommen." noSuchAnnouncement: "&4Dieser Announcer existiert nicht." noSuchDungeon: "&4Dieser Dungeon existiert nicht." noSuchGroup: "&4Die Gruppe &6&v1&4 gibt es nicht." noSuchMap: "&4Die Welt &6&v1&4 gibt es nicht." noSuchPlayer: "&4Den Spieler &6&v1&4 gibt es nicht." noSuchResourcePack: "&4Das Ressourcenpaket &6&v1 &4ist in der Hauptkonfigurationsdatei nicht registriert." noSuchShop: "&4Der Shop &v1 &4wurde nicht gefunden..." notInDungeon: "&4Du bist nicht in einem Dungeon." notInGame: "&4Die Gruppe &6&v1&4 ist kein Mitglied des Spiels." notInGroup: "&4Der Spieler &6&v1&4 ist kein Mitglied der Gruppe &6&v2&v4." notInvited: "&4Du wurdest in die Gruppe &6&v1&4 nicht eingeladen." notLeader: "&4Du bist nicht der Kapitän der Gruppe." notSaved: "&4Die Welt &6&v1&4 wurde noch nicht im &6DungeonsXL/maps/&4-Ordner abgespeichert." ready: "&4Wähle erst Deine Klasse." requirements: "&4Du erfüllst nicht alle Voraussetzungen, um diesen Dungeon zu spielen." selfNotInGroup: "&4Du bist in keiner Gruppe." signWrongFormat: "&4Das Schild ist nicht richtig beschriftet." tooManyInstances: "&4Im Moment sind zu viele Welten instanziert. Versuche es in ein paar Minuten nochmal." tooManyTutorials: "&4Im Moment laufen zu viele Tutorials. Versuche es in ein paar Minuten nochmal." tutorialDoesNotExist: "&4Der Tutorial-Dungeon existiert nicht." group: bedDestroyed: "&6Das Bett der Gruppe &4&v1 &6wurde von &4&v2&6 zerstört." congrats: "&6Herzlichen Glückwunsch!" congratsSub: "&l&4Deine Gruppe &v1 &4hat das Spiel gewonnen." created: "&4&v1&6 hat die Gruppe &4&v2&6 erstellt." death: "&4&v1 &6ist gestorben. &4&v2 &6haben jetzt &4&v3 &6Leben übrig." deathKick: "&4&v1 &6wurde gekickt, weil &4&v2 &6keine Leben mehr übrig haben." defeated: "&4Die Gruppe &4v1 &6wurde besiegt, weil sie ihren letzten Punkt verloren hat." disbanded: "&4&v1&6 hat die Gruppe &4&v2&6 aufgelöst." flagCaptured: "&4&v1&6 hat die Flagge der Gruppe &4&v2&6 gestohlen." flagLost: "&4&v1&6 ist gestorben und hat die Flagge der Gruppe &4&v2&6 verloren." flagStealing: "&4&v1&6 stiehlt die Flagge der Gruppe &4&v2&6." invitedPlayer: "&4&v1&6 hat den Spieler &4&v2&6 in die Gruppe &4&v3&6 eingeladen." joinedGame: "&6Deine Gruppe ist dem Spiel erfolgreich beigetreten." kickedPlayer: "&4&v1&6 hat den Spieler &4&v2&6 aus der Gruppe &4&v3&6 herausgeworfen." killed: "&4&v1 &6wurde von &4&v2&6 getötet. &4&v3&6 haben jetzt &4&v4 &6Leben übrig." killedKick: "&4&v1&6 wurde von &4&v2&6 getötet. &4&v3 haben keine Leben mehr übrig." livesAdded: "&6Deine Gruppe hat &4&v1&6 Extraleben bekommen." livesRemoved: "&6Deine Gruppe hat &4&v1&6 Leben verloren." playerJoined: "&6Der Spieler &4&v1&6 hat die Gruppe betreten." rewardChest: "&6Deine Gruppe hat eine Schatztruhe gefunden." uninvitedPlayer: "&4&v1&6 hat die Einladung für &4&v2&6 in die Gruppe &4&v3&6 zurückgezogen." waveFinished: "&6Deine Gruppe hat die &4&v1&6te Welle beendet. Die nächste fängt in &4&v2&6 Sekunden an." player: blockInfo: "&6Block-ID:&2&v1" checkpointReached: "&6Checkpoint erreicht." death: "&4&v1 &6ist gestorben und hat &4&v2 &6Leben übrig." deathKick: "&2&v1 &6hat sein letztes Leben verloren und wurde aus dem Spiel geworfen." finishedDungeon: "&6Du hast den Dungeon erfolgreich beendet." finished_Floor: "&6Du hast das Level erfolgreich beendet." invited: "&4&v1&6 hat Dich eingeladen, der Gruppe &4&v2&6 beizutreten." joinGroup: "&6Du bist der Gruppe erfolgreich beigetreten." kicked: "&4Du wurdest aus der Gruppe &6&v1&4 herausgeworfen." killed: "&4&v1 &6wurde von &4&v2 &6getötet und hat &4&v3 &6Leben übrig." killedKick: "&4&v1&6 wurde von &4&v2 &6getötet und hat das letzte Leben verloren." leaveGroup: "&6Du hast die Gruppe erfolgreich verlassen." leftGroup: "&6Der Spieler &4&v1&6 hat die Gruppe verlassen." livesAdded: "&6Du hast &4&v1&6 Extraleben bekommen." livesRemoved: "&6Du hast &4&v1&6 Leben verloren." lootAdded: "&4&v1&6 wurden Deinem Belohnungsinventar hinzugefügt." newLeader: "&6Du bist jetzt der neue Kapitän Deiner Gruppe." offline: "&6Der Spieler &4&v1&6 hat den Server verlassen. In &4&v2&6 Sekunden wird er automatisch aus dem Spiel geworfen." offlineNever: "&6Der Spieler &4&v1&6 hat den Server verlassen. Er wird aber &4nicht&6 automatisch aus dem Spiel geworfen." portal: abort: "&6Das Erstellen des Portals wurde abgebrochen." created: "&6Das Portal wurde erstellt." introduction: "&6Klicke die zwei Ecken des Portals mit dem Holzschwert an." progress: "&6Das erste Eck wurde erfolgreich markiert. Klicke nun das zweite Eck an." rotate: "&6Möchtest Du das Portal drehen?" protectedBlockDeleted: "&6Die Sicherung wurde erfolgreich entfernt." ready: "&6Du bist jetzt bereit, das Spiel zu starten." signCopied: "&6Schilddaten kopiert." signCreated: "&6Das Dungeon-Schild wurde erfolgreich erstellt." timeKick: "&6Die Zeit von &2&v1&6 ist abgelaufen." timeLeft: "&v1Du hast &6&v2 &v1Sekunden übrig, um den Dungeon zu beenden." treasures: "&1Schätze" uninvited: "&4&v1&6 hat Deine Einladung zur Gruppe &4&v2&6 zurückgezogen." unlimitedLives: "unendlich viele" waitForOtherPlayers: "&6Warte auf andere Gruppenmitglieder..." requirement: fee: "&6Du hast &4&v1 &6bezahlt, um den Dungeon zu betreten." feeItems: "Items" feeLevel: "Level" feeMoney: "Geld" finishedDungeons: and: "und" name: "Abgeschlossene Dungeons" or: "oder" withinTime: "innerhalb der letzten &v1 Stunden" forbiddenItems: "Verbotene Items" groupSize: "Gruppengröße" keyItems: "Schlüssel" permission: "Permissions" timeSince: finish: "Ausreichend vergangene Zeit seit dem letzten abgeschlossenen Spiel (&v1 Stunden)" start: "Ausreichend vergangene Zeit seit dem letzten Versuch (&v1 Stunden)" never: "Bisher keine Versuche." timeframe: "Zeitfenster" reward: general: "&6Du hast &4&v1 &6für das Beenden des Dungeons erhalten." sign: end: "&aENDE" floor: 1: "&aNÄCHSTES LEVEL" 2: "&aBETRETEN" global: full: "&4VOLL" isPlaying: "&4AM SPIELEN" joinGame: "&aSPIEL BEITRETEN" joinGroup: "&aGRUPPE BEITRETEN" newGame: "&aNEUES SPIEL" newGroup: "&aNEUE GRUPPE" leave: "&aVERLASSEN" ready: "&aBEREIT" resourcePack: "&aDOWNLOAD" wave: 1: "&aBEGINNE" 2: "&aNÄCHSTE WELLE" ================================================ FILE: core/src/main/resources/plugin.yml ================================================ name: ${project.parent.name} main: de.erethon.dungeonsxl.DungeonsXL version: ${project.parent.version}${buildNo} authors: [Frank Baumann, Milan Albrecht, Tobias Schmitz, Daniel Saukel] description: ${project.parent.description} website: ${project.parent.url} depend: [XLib-Runtime] softdepend: [CommandsXL, Vault, Citizens, CustomMobs, InsaneMobs, MythicMobs, HolographicDisplays, LWC, PlaceholderAPI, Parties] commands: dungeonsxl: description: Reference command for DungeonsXL. aliases: [dxl,dungeon] api-version: 1.13 ================================================ FILE: dist/pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-dist ${project.parent.version} jar de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT ${project.artifactId}-${project.version}${buildNo} org.apache.maven.plugins maven-shade-plugin 3.6.1 package shade de.erethon.dungeonsxl:* de.erethon.dungeonsxl dungeonsxl-adapter ${project.parent.version} de.erethon.dungeonsxl dungeonsxl-api ${project.parent.version} de.erethon.dungeonsxl dungeonsxl-bukkit_blockdata ${project.parent.version} de.erethon.dungeonsxl dungeonsxl-bukkit_magicvalues ${project.parent.version} de.erethon.dungeonsxl dungeonsxl-core ${project.parent.version} ================================================ FILE: pom.xml ================================================ 4.0.0 de.erethon.dungeonsxl dungeonsxl-parent 0.19-SNAPSHOT pom DungeonsXL https://dre2n.github.io Create custom dungeons and adventure maps with ease! adapter api bukkit_blockdata bukkit_magicvalues core dist UTF-8 1.8 1.8 26.1.2-R0.1-SNAPSHOT 7.0-SNAPSHOT de.erethon.xlib xlib-api ${dependencyVersion.xlib} provided spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ dre-repo https://erethon.de/repo